Skip to content

Fix catchCause does not deliver interrupt causes when fiber is interruptible - #7140

Closed
fubhy wants to merge 2 commits into
mainfrom
audit/repro-f0005-catchcause-interrupt
Closed

Fix catchCause does not deliver interrupt causes when fiber is interruptible#7140
fubhy wants to merge 2 commits into
mainfrom
audit/repro-f0005-catchcause-interrupt

Conversation

@fubhy

@fubhy fubhy commented Aug 7, 2026

Copy link
Copy Markdown
Member

Summary

  • deliver external interruption causes to failure continuations such as Effect.catchCause
  • continue unwinding after invoking skipped continuations so handler effects cannot recover from external interruption
  • replace the audit reproduction with a focused cause assertion and update workflow suspension expectations
  • add a patch changeset for effect

Testing

  • pnpm test --run --project effect --silent=passed-only --reporter=dot
  • pnpm --filter effect check
  • pnpm dprint check packages/effect/src/internal/core.ts packages/effect/test/Effect.test.ts packages/effect/test/cluster/ClusterWorkflowEngine.test.ts .changeset/catch-cause-interrupts.md
  • pnpm oxlint packages/effect/src/internal/core.ts packages/effect/test/Effect.test.ts packages/effect/test/cluster/ClusterWorkflowEngine.test.ts

Closes EFF-571

@fubhy fubhy added bug Something isn't working audit Findings originating from the Effect runtime correctness audit labels Aug 7, 2026
@effect-slopcop effect-slopcop Bot added the 4.0 label Aug 7, 2026
@fubhy
fubhy force-pushed the audit/repro-f0005-catchcause-interrupt branch from 350dc4d to 94e6012 Compare August 7, 2026 16:40
@changeset-bot

changeset-bot Bot commented Aug 7, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e0ffce5

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
Name Type
effect Patch
@effect/ai-anthropic Patch
@effect/ai-openai Patch
@effect/ai-openai-compat Patch
@effect/ai-openrouter Patch
@effect/atom-react Patch
@effect/atom-solid Patch
@effect/atom-vue Patch
@effect/docgen Patch
@effect/doctest Patch
@effect/openapi-generator Patch
@effect/opentelemetry Patch
@effect/platform-browser Patch
@effect/platform-bun Patch
@effect/platform-deno Patch
@effect/platform-node Patch
@effect/platform-node-shared Patch
@effect/sql-clickhouse Patch
@effect/sql-d1 Patch
@effect/sql-libsql Patch
@effect/sql-mssql Patch
@effect/sql-mysql2 Patch
@effect/sql-pg Patch
@effect/sql-pglite Patch
@effect/sql-sqlite-bun Patch
@effect/sql-sqlite-do Patch
@effect/sql-sqlite-node Patch
@effect/sql-sqlite-react-native Patch
@effect/sql-sqlite-wasm Patch
@effect/vitest Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

This PR documents a real bug with a failing reproduction test, but the runtime fix described in the title and changeset is not present. The new test fails because packages/effect/src/internal/core.ts was not modified.

Reviewed changes

  • Added a changeset claiming catchCause now delivers interrupt causes in interruptible fibers.
  • Added a new test in packages/effect/test/Effect.test.ts that reproduces the bug.

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Fix it ➔View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The latest commit (94e6012) is identical in content to the previously reviewed commit and still does not contain the runtime fix in packages/effect/src/internal/core.ts. The prior feedback stands: please implement the exitFailCause loop change so the new test can pass.

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Fix it ➔View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@fubhy fubhy added the slop label Aug 7, 2026
@fubhy
fubhy force-pushed the audit/repro-f0005-catchcause-interrupt branch from 94e6012 to 2b176b1 Compare August 7, 2026 16:52
@effect-slopcop effect-slopcop Bot added 3.0 Used for issues, pull requests, etc. that are relevant for the `v3` branch targeting Effect v3. 4.0 and removed 4.0 3.0 Used for issues, pull requests, etc. that are relevant for the `v3` branch targeting Effect v3. labels Aug 7, 2026

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No critical issues — minor suggestions inline.

Reviewed changes

  • Removed the .changeset/tricky-bugs-check.md that claimed a runtime fix was shipped.
  • Updated the PR description to clarify that this is a test-only regression repro with no implementation fix.
  • Added the "receives interrupt causes on interruption" regression test inside the catchCause describe block.

The prior CHANGES_REQUESTED feedback is addressed by narrowing the scope to a failing regression test. One small formatting fix is still needed before merge.

Note: the PR title still says "Fix ..." while the body now says the change is test-only. Consider aligning the title with the actual scope (e.g., "Add regression test for ...").

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Fix all ➔Fix 👍s ➔View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

Comment thread packages/effect/test/Effect.test.ts Outdated
Comment thread packages/effect/test/Effect.test.ts Outdated
@tim-smart tim-smart closed this Aug 8, 2026

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The fix delivers the interrupt cause to catchCause/matchCauseEffect legacy handlers, but in the pop loop the handler's returned Effect is discarded (only its synchronous closure runs). So the most common handler idiom — catchCause(cause => Effect.log(cause)) — constructs the effect and never evaluates it; only inline mutations (like the new test's caught = cause) are observed. Please confirm this matches what EFF-571 needs: if handlers should actually react to the interruption, the continuation's result must flow back into the run loop instead of being dropped.

Reviewed changes

  • Implemented the runtime fix in exitFailCause (packages/effect/src/internal/core.ts): the interrupt-unwind pop loop now invokes each skipped failure continuation so OnFailure / OnSuccessAndFailure handlers receive the interruption cause.
  • Added the catch-cause-interrupts patch changeset for effect.
  • Rewrote the regression test in packages/effect/test/Effect.test.ts to capture and assert the delivered cause (Cause.hasInterruptsOnly), replacing the earlier boolean flag.
  • Updated packages/effect/test/cluster/ClusterWorkflowEngine.test.ts: catchCause is now expected to run on the suspension interrupt.

Prior Pullfrog threads (test assertion form and dprint) are resolved by this commit.

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

}
let cont = fiber.getCont(contE)
while (fiber.interruptible && fiber._interruptedCause && cont) {
cont[contE](cause, fiber, annotated ? undefined : this)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During interruption, this loop invokes each delivery target's [contE] but throws away its return value, then pops the next continuation. For a bare catchCause/matchCauseEffect (no contAll finalizer on top), fiber.interruptible stays true and the loop calls the handler, then discards the Effect it returns and ends in fiber.yieldWith(this). So Effect.catchCause(cause => Effect.log(cause)) — the common idiom — has its closure run but the log effect is never evaluated; only inline synchronous mutations (like the new test's caught = cause) are observable. If EFF-571 is about letting users observe/record the interruption, this doesn't deliver that for effect-returning handlers. If instead handlers must not act at all during interruption, consider whether invoking them at all (with side-effect-free result) is the intended contract, and document it.

expect(flags.get("ensuring")).toBeTruthy()
expect(flags.get("catchCause")).toBeFalsy()
// catchCause receives the suspension interrupt
expect(flags.get("catchCause")).toBeTruthy()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This flip (and the one at line 75) is the observable contract change this PR ships. It's consistent with the fix (the workflow's catchCause handler at line 518 does a synchronous flags.set, which survives the discarded-return semantics), just noting it since a behavior-sensitive reviewer may want it called out in the changeset as more than a one-line fix description.

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File Name Current Size Previous Size Difference
basic.ts 6.92 KB 6.92 KB +0.01 KB (+0.07%)
batching.ts 9.73 KB 9.72 KB +0.00 KB (+0.04%)
brand.ts 6.61 KB 6.60 KB +0.01 KB (+0.09%)
cache.ts 10.60 KB 10.59 KB +0.01 KB (+0.06%)
config.ts 20.92 KB 20.91 KB +0.01 KB (+0.03%)
differ.ts 19.78 KB 19.77 KB +0.01 KB (+0.04%)
http-client.ts 21.52 KB 21.52 KB +0.01 KB (+0.04%)
logger.ts 10.81 KB 10.81 KB +0.01 KB (+0.05%)
metric.ts 8.87 KB 8.86 KB +0.01 KB (+0.08%)
optic.ts 6.69 KB 6.68 KB +0.01 KB (+0.09%)
pubsub.ts 14.87 KB 14.86 KB +0.01 KB (+0.05%)
queue.ts 11.54 KB 11.54 KB +0.01 KB (+0.06%)
schedule.ts 10.72 KB 10.71 KB +0.01 KB (+0.07%)
schema-class.ts 19.49 KB 19.48 KB +0.01 KB (+0.04%)
schema-fromJsonSchemaDocument.ts 29.37 KB 29.36 KB +0.01 KB (+0.02%)
schema-representation-roundtrip.ts 25.64 KB 25.63 KB +0.01 KB (+0.02%)
schema-string-transformation.ts 13.55 KB 13.55 KB +0.01 KB (+0.04%)
schema-string.ts 11.10 KB 11.09 KB +0.01 KB (+0.06%)
schema-template-literal.ts 15.39 KB 15.38 KB +0.01 KB (+0.05%)
schema-toArbitrary.ts 21.53 KB 21.52 KB +0.01 KB (+0.03%)
schema-toCodeDocument.ts 24.00 KB 24.00 KB +0.01 KB (+0.03%)
schema-toCodecJson.ts 18.75 KB 18.74 KB +0.01 KB (+0.03%)
schema-toEquivalence.ts 18.57 KB 18.57 KB +0.01 KB (+0.03%)
schema-toFormatter.ts 18.44 KB 18.43 KB +0.01 KB (+0.03%)
schema-toJsonSchemaDocument.ts 22.59 KB 22.59 KB +0.01 KB (+0.03%)
schema-toRepresentation.ts 19.08 KB 19.08 KB +0.01 KB (+0.03%)
schema.ts 18.74 KB 18.73 KB +0.01 KB (+0.03%)
stm.ts 12.60 KB 12.59 KB +0.01 KB (+0.04%)
stream.ts 9.67 KB 9.67 KB +0.01 KB (+0.07%)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.0 audit Findings originating from the Effect runtime correctness audit bug Something isn't working slop

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants