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

Relationships

#2514 A suspended run started by swamp serve cannot be cancelled

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

Problem

A run started through swamp serve records its serve instance (instanceId). From the dashboard, workflow run --server, or a webhook, it is set by run.start(Deno.pid, ctx.instanceId) (src/serve/handlers/workflow_handlers.ts, src/serve/webhook.ts). Once such a run is suspended, nothing can cancel it:

  • Local swamp workflow cancel <wf> --run <id> refuses any serve-owned run, whatever its status: "belongs to a serve instance and cannot be cancelled locally. Use --server" (src/cli/commands/workflow_cancel.ts, isServeOwnedRun).
  • swamp workflow cancel --run <id> --server <url> and the WebSocket cancel look only at in-memory registries: cancelRegistry, activeRunRegistry, and scheduled runs (cancelExecution in src/cli/commands/serve.ts; src/serve/connection.ts). A suspended run leaves those when it suspends, so the request returns 404 "No active workflow-run with id … in this serve instance".
  • Supersede skips serve-owned runs (src/libswamp/workflows/supersede.ts).
  • reject needs a gate in waiting_approval, so it cannot clear a run whose gates are already approved.

The design doc says to "cancel those through the serve API" (supersede) and, for an expired gate, "The run stays suspended; cancel it to clear it." Neither works for a serve-owned run.

Where it shows up

  • An expired approval gate on a serve-started run: approve and reject are both refused, and the run stays suspended for good.
  • swamp-club#2498: resume refuses a suspended run whose workflow changed shape. For a serve-owned run, the refusal can only say to revert the workflow change and resume, because no cancel path works.
  • Clutter: a stuck run keeps appearing in swamp workflow approvals and on the dashboard with a Resume action. A later suspended run of the same workflow then makes a bare approve/resume fail with "Multiple suspended runs".

Suggested fix

Have serve's cancel (HTTP and WebSocket) fall back to a persisted suspended run it owns. Load it from the run repository, then run.cancel(reason) and save. That needs a way to find a run by id alone, since the cancel request carries no workflow name. It also needs the same authorization and audit as cancelling an active run.

If local cancel is instead allowed for a serve-owned run that is suspended, it must not call killProcessTree(run.pid). That pid is the serve process, which passes the isSwampProcess check. Local cancel also races an auto-resume that serve may be launching at that moment.

Once a fix lands, update the #2498 refusal for serve-owned runs (suspendedWayOut in src/domain/workflows/resume_reset.ts) to name the working cancel command.

Acceptance

  • A serve-owned suspended run can be cancelled through serve, over both HTTP and WebSocket, and the cancellation is audited.
  • An expired-gate run started from the dashboard can be cleared.
  • The #2498 refusal for serve-owned runs names that command.
02Bog Flow
✓OPEN✓TRIAGED✓IN PROGRESS✓SHIPPED+ 1 MOREASSIGNED+ 7 MOREREVIEW+ 27 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

9/28/2026, 11:49:26 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
hammz assigned hammz9/28/2026, 8:53:24 PM

Sign in to post a ripple.