Skip to content

Close scoped resource cancellation and cause-translation gaps #30

Description

@endalk200

Finding

Two lower-frequency resource/error-boundary gaps were found while tracing Effect beta.100:

  1. The isolated query Worker is created and initialized before its close finalizer is registered. Interruption during initialization can leave an unowned Worker in a long-lived embedding runtime.
  2. Broad catchCause handlers translate typed failures, defects, and interruption into ordinary availability errors, hiding cancellation and programmer defects.

Normal CLI signal handling exits the process, so this is lower priority than the SQLite failure-channel issue. It still weakens the documented scoped-ownership model.

Analysis chain

  1. Architecture docs say Scopes own workers, listeners, SQLite clients, and signals.
  2. I traced acquisition/finalizer ordering for query workers.
  3. Query initialization can create a Worker before Scope cleanup is registered.
  4. Effect beta.100 tryPromise gives cancellation signaling only when its callback accepts a signal; this zero-argument initializer does not.
  5. Interruption in that window can abandon transport ownership.
  6. I traced broad catchCause boundaries and confirmed they include failure, defect, and interruption.
  7. Those categories are collapsed into generic availability failures, reducing diagnostic fidelity.

Evidence

Version-matched Effect 4.0.0-beta.100:

  • effect/src/internal/effect.ts:1043-1118 creates an AbortController only when tryPromise accepts a signal.
  • effect/src/Effect.ts:3212-3228 defines catchCause across typed failure, defect, and interruption.

Practical scope:

  • Ordinary CLI shutdown exits the process and kills the Worker.
  • Programmatic cancellation, tests, or future long-lived embedding can retain it.
  • Broad cause translation already weakens ordinary startup diagnostics.

Fix suggestion

  • Acquire query transport with acquireRelease or register close immediately before initialization.
  • Make initialize explicitly cancellable and dispose partial Worker state on abort.
  • Translate expected errors with typed handlers.
  • Preserve interruption identity.
  • Preserve/log original Causes for recognized environmental defects.
  • Re-defect on unexpected invariants rather than reporting ordinary unavailability.
  • Keep user messages concise while retaining internal diagnostic causes.

Acceptance criteria

  • Interruption at every initialization phase leaves no Worker, listener, or reader behind.
  • Initialization has an explicit cancellation path.
  • Expected typed failures retain stable public codes.
  • Interruption remains interruption across startup boundaries.
  • Unexpected defects retain/log their original Cause.
  • Tests interrupt acquisition before Worker creation, during initialization, and just before readiness.
  • Graceful shutdown and worker restart remain intact.
  • Formatting, types, lint, and tests pass.

Blocked by

None — can start immediately.

Audit context

This intentionally groups the two lower-priority scoped ownership/cause-translation observations; the higher-impact SQLite defect channel is tracked separately.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingneeds-triageMaintainer needs to evaluate this issue

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions