Skip to main content
← Back to list
01Issue
BugShippedSwamp CLIPublic
Assigneeshammz

Relationships

#2432 workflow resume prints unexpected errors as a fatal stack trace, and --json returns them unclassified with the stack

Opened by hammz · 9/23/2026· Shipped 9/24/2026

Problem

When swamp workflow resume fails with an error that is not a UserError, it prints as a fatal error with a full stack trace. swamp workflow run reports the same errors as a single line.

With the same bad input on the same workflow (a step argument of ${{ inputs.code + 0 }} and a string code):

  • swamp workflow run <wf> --input code=notanumber prints Error: Workflow execution failed: Invalid expression: no such overload: dyn<string> + int
  • swamp workflow resume <wf> --run <id> --from <step> --input code=notanumber prints [FTL] error: InvalidExpressionError: Invalid expression: no such overload: dyn<string> + int followed by about 20 lines of stack, and a nested [cause] block with its own stack.

--json mode differs the same way:

  • run writes {"error": "Workflow execution failed: ...", "code": "workflow_execution_failed"}.
  • resume writes {"error": "...", "stack": " at CelEvaluator.evaluateAsync (file:///tmp/deno-compile-swamp/src/...)..."} with no code, so a caller cannot classify the failure and gets internal source paths instead.

Any error from the resume stream that is not a UserError shows up this way. Another way to hit it is --from on a step that was renamed since the run failed, which prints [FTL] error: Error: Step run not found: <step> with a stack trace. The --from behavior itself is a separate problem.

This affects suspended resumes, --from, and the retry of failed runs added by swamp-club#2409, on main (c811f8ce) and on that branch.

Cause

src/cli/commands/workflow_resume.ts consumes the resume stream inside try/finally with no catch, so these errors reach the top-level handler, which prints anything that is not a UserError as fatal with its stack. src/cli/commands/workflow_run.ts catches errors from its execution stream and rethrows them as UserError("Workflow execution failed: …").

Fix approach

Handle errors from the resume stream the way workflow run does: rethrow them as a UserError with a resume-specific message and error code, so log mode prints one line and --json mode returns a classified error without a stack. Check the --server path for the same gap.

Reproduction

  1. Create a workflow with step prep, then a step check that depends on it and whose shell run is exit ${{ inputs.code + 0 }}.
  2. swamp workflow run <wf> --input '{"code":3}': check fails with exit code 3. Note the run id.
  3. swamp workflow resume <wf> --run <id> --from check --input code=notanumber (on the #2409 branch, --from can be dropped).
  4. The resume exits 1 with an [FTL] InvalidExpressionError and stack trace. Add --json to see the {"error","stack"} shape.
02Bog Flow
✓OPEN✓TRIAGED✓IN PROGRESS✓SHIPPED+ 1 MOREASSIGNED+ 5 MOREREVIEW+ 23 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

9/24/2026, 2:20:52 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
hammz assigned hammz9/23/2026, 7:31:21 PM

Sign in to post a ripple.