Skip to content

Dispatcher drops non-StandardError under concurrent worker failures #208

Description

@duncanita

Severity: medium
Found in: 2026-07-22 code review

Location

lib/dag/effects/dispatcher.rb:224 (worker_errors.compact.first) interacting with tick at :167-170.

Failure mode

When two workers fail in the same tick, the "winning" exception is chosen by worker index, not by severity or chronology. If worker 0 captures a StandardError (storage failure) and worker 1 captures a non-StandardError (Interrupt, SystemExit, NoMemoryError), .first yields the StandardError, so tick raises DispatchAbortedError — itself a StandardError — and the fatal exception is dropped entirely (it is not even set as #cause). The documented contract "Non-StandardError exceptions propagate unwrapped" is violated, and an upstream rescue => e will swallow what should have been a process-fatal signal.

Minimal trigger (reproduced)

parallelism: 2 against a thread_safe_for_dispatch? adapter; record 0's complete_effect_succeeded raises RuntimeError; record 1's handler raises Interrupt after a small sleep. tick raises DispatchAbortedError wrapping the RuntimeError; the Interrupt vanishes.

Fix direction

When selecting among captured worker errors, prefer non-StandardError (propagate it unwrapped); optionally also prefer chronological first-failure among same-severity errors (see companion issue on #cause attribution).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingeffectsEffect-aware kernel protocol and durable intent work

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions