Skip to content

credit pipeline: surface mid-run issuance-task exceptions instead of swallowing them (follow-up to #1039) #1041

Description

@ajcasagrande

Summary

Follow-up to #1039. That PR closed the startup instance of a broader bug: exceptions raised inside the fire-and-forget execute_async(strategy.execute_phase()) task in the timing/credit pipeline are never retrieved, so a failure there silently stalls the run instead of failing it.

#1039 added a per-phase StickyCreditRouter.wait_for_workers() barrier so the first credit is only issued after a worker has registered — closing the "no worker registered yet" deadlock. But the general swallowed-exception class remains.

The general problem

send_credit() stays fail-fast for the real no-workers-mid-run case, and other exceptions can still arise inside the issuance task. Because the task is fire-and-forget and its result is never awaited/observed, any such exception is swallowed:

  • all_credits_sent_event (and/or all_credits_returned_event) never gets set.
  • _wait_for_sending_complete() awaits forever → the run hangs with no error surfaced.

Known residual triggers (called out in #1039's description and review):

  1. The last worker unregisters after the wait_for_workers barrier returns but before the first send_credit()send_credit() raises "No workers available", which is swallowed.
  2. Any other exception inside strategy.execute_phase() after issuance begins.

Expected behavior

The issuance task's exception should be observed and routed to the phase-failure path (the same mechanism _on_phase_orchestrator_done / _publish_phase_failure uses for phase-setup failures), so the run exits non-zero with a clear error instead of hanging.

Options to consider:

  • Attach a done-callback to the execute_async(strategy.execute_phase()) task that inspects task.exception() and drives the phase-failure path.
  • Or set the completion events in a finally/error path so _wait_for_sending_complete() can't await forever, and record the error.

Acceptance

  • An exception raised inside the issuance task fails the run (non-zero exit, BaseServiceErrorMessage recorded) rather than hanging.
  • Regression test: inject a send_credit/execute_phase failure mid-run and assert the run fails fast with the error surfaced (no hang).

Context

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions