coleam00/Archon · error
Failed to cancel workflow run: ${err.message}
Error message
Failed to cancel workflow run: ${err.message} What it means
cancelWorkflowRun() sets status='cancelled' unless the run is already 'completed' or 'cancelled' (a 'failed' run remains cancellable). This wrapper rethrows database errors from the transaction with the original message appended. A zero-row update is NOT an error here — it returns { cancelled: false } as an idempotent no-op.
Source
Thrown at packages/core/src/db/workflows.ts:1340
`UPDATE remote_agent_workflow_runs
SET status = 'cancelled', completed_at = ${dialect.now()}
WHERE id = $1 AND status NOT IN ('completed', 'cancelled')`,
[id]
);
if ((update.rowCount ?? 0) > 0) {
await insertWorkflowEvent(query, {
workflow_run_id: id,
event_type: 'workflow_cancelled',
step_name: event?.step_name,
data: event?.reason === undefined ? undefined : { reason: event.reason },
});
}
return update;
});
} catch (error) {
const err = error as Error;
getLog().error({ err }, 'db.workflow_run_cancel_failed');
throw new Error(`Failed to cancel workflow run: ${err.message}`);
}
const cancelled = (result.rowCount ?? 0) > 0;
if (!cancelled) {
// Idempotent no-op: the run was already terminal. Returned so callers can
// report "nothing to cancel" instead of a false "Cancelled" (see #1830 I1).
// Same info level as the resume CAS-miss signal for consistency (S2).
getLog().info({ workflowRunId: id }, 'db.workflow_run_cancel_noop');
}
return { cancelled };
}
export async function cancelFanOutRun(
id: string,
reason: FanOutCancelReason
): Promise<{ cancelled: boolean }> {
const dialect = getDialect();
let result: Awaited<ReturnType<IDatabase['query']>>;
try {View on GitHub (pinned to 0773b97458)
Solutions
- Check the appended underlying message and the 'db.workflow_run_cancel_failed' log for the root cause
- Verify database connectivity, then reissue the cancel — it is safe to retry (terminal rows are guarded, repeat cancels return cancelled:false)
- Confirm the database schema matches the binary version (apply migrations)
- If event details were passed, ensure step_name/reason are simple strings matching WorkflowCancellationEventDetails
- Use the returned { cancelled } flag rather than assuming the run stopped; the executor honors cancellation cooperatively
Example fix
// before
await cancelWorkflowRun(id, { step_name: 'deploy', reason: 42 } as any);
// after
const { cancelled } = await cancelWorkflowRun(id, { step_name: 'deploy', reason: 'user requested' });
if (!cancelled) getLog().info({ runId: id }, 'run already terminal; nothing to cancel'); Defensive patterns
Strategy: try-catch
Validate before calling
const run = await getWorkflowRun(id);
if (!run) throw new Error(`run ${id} does not exist`);
if (['completed', 'cancelled'].includes(run.status)) {
return { cancelled: false }; // cancel would be a no-op anyway
} Type guard
function isCancellationEvent(v: unknown): v is WorkflowCancellationEventDetails {
const e = v as WorkflowCancellationEventDetails;
return (e.step_name === undefined || typeof e.step_name === 'string') &&
(e.reason === undefined || typeof e.reason === 'string');
} Try / catch
try {
const { cancelled } = await cancelWorkflowRun(id, event);
if (!cancelled) getLog().info({ runId: id }, 'nothing to cancel');
} catch (err) {
getLog().error({ err, runId: id }, 'cancel db error');
await backoffThenRetry(() => cancelWorkflowRun(id)); // retry is safe: terminal rows are guarded
} Prevention
- Rely on the { cancelled } return value instead of assuming success
- Retry cancels safely — the guard makes repeat calls idempotent
- Keep schema/migrations current so the workflow_events insert cannot fail
- Pass only string step_name/reason values in event details
- Remember 'failed' runs are still cancellable; 'completed'/'cancelled' are not
When it happens
Trigger: Calling cancelWorkflowRun(id, event?) when the UPDATE or the workflow_cancelled event insert throws: DB connection failure, constraint violation on the event insert, dialect JSON/now() failure, or transaction rollback/deadlock.
Common situations: Database unreachable or restarted while an operator or executor issues a cancel; workflow_events table missing a newly added column (schema drift between binary and database); long-running transaction contention on the run row; passing an event detail object with an unexpected shape that breaks the insert.
Related errors
- Failed to cancel fan-out run: ${err.message}
- Failed to get workflow run: ${err.message}
- Workflow run '${runId}' references codebase '${codebaseId}',
- Failed to load codebase '${codebaseId}' for workflow run '${
- Failed to complete workflow run: ${err.message}
AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01).
Data as JSON: /api/errors/5069455cd9db7d9a.
Report an issue: GitHub.