Relationships
#2430 workflow resume: Ctrl-C leaves the run 'running' with nothing driving it, and it can no longer be resumed
Opened by hammz · 9/23/2026· Shipped 9/23/2026
Problem
Pressing Ctrl-C during swamp workflow resume exits the process with code 130 but leaves the run record at running. The step that was executing and its dependents stay pending, and the run tracker row stays running. Nothing is driving the run, and it cannot be resumed again: resume --run <id> and resume --from <step> --run <id> both refuse a running run.
This affects every local resume: a suspended run after approval, --from on a failed run, and the retry of a failed run added by swamp-club#2409. The retry makes it easier to hit, since every failed run now prints a swamp workflow resume <wf> --run <id> command to run next.
A fresh swamp workflow run handles the same Ctrl-C: it records the run as cancelled and exits 1.
The only way out today is swamp workflow cancel <wf> --run <id>, which moves the run to cancelled. A cancelled run cannot be retried or resumed with --from, so the steps that already succeeded are lost and the operator has to start a new run.
Cause
workflow run suppresses the datastore sync coordinator's Deno.exit(130) on SIGINT (suppressSyncExitOnSignal()), and when its abort signal fires it saves the run as cancelled before exiting. workflow resume (src/cli/commands/workflow_resume.ts) registers a shutdown handler that aborts its controller, but it does not suppress that exit, so the process exits before resume() can save anything. It also has no fallback that saves a terminal status for the run after an abort.
Expected
Ctrl-C during a resume leaves the run in a terminal state, and the tracker row agrees with it. workflow run uses cancelled. For a resume, consider whether an operator interrupt should leave the run retryable (for example failed, with the interrupted step recorded), because cancelled discards a run the operator explicitly chose to continue.
Fix approach
Give the local resume path the same SIGINT handling as workflow run: keep the process alive after SIGINT, let resume() unwind on the abort signal, save the chosen terminal status if the generator did not, and complete the tracker row before exiting non-zero. Check the remote path (resume --server) separately.
Reproduction (Linux)
- Create a workflow with a step
sthat runssleep 6; test -f /tmp/repro/okand a steptthat depends ons. swamp workflow run slow:sfails. Note the run id.touch /tmp/repro/ok, thenswamp workflow resume slow --run <id>(or--from s --run <id>, or approve a suspended run andswamp workflow resume <wf>).- Press Ctrl-C while
sis running. The process exits 130. swamp workflow history get slow --json: statusrunning,sandtpending.swamp run history --activestill lists the run asrunning.swamp workflow resume slow --run <id>refuses:Run <id> is not suspended or failed (status: running).
Reproduced on main at c811f8ce for the --from and post-approval resumes, and on the swamp-club#2409 branch for all three.
Related: swamp-club#2420 (a resume keeps the original run's process identity, so workflow cancel cannot stop a live resume).
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.