coleam00/Archon · error
Workflow run not found or already terminal (id: ${id})
Error message
Workflow run not found or already terminal (id: ${id}) What it means
failWorkflowRun() updates only rows whose status is IN ('running','pending'); terminal rows (completed/failed/cancelled) never transition. If rowCount is 0 — id unknown or run already terminal — this error is thrown so callers never silently re-fail or re-stamp a finished run.
Source
Thrown at packages/core/src/db/workflows.ts:1302
});
}
if ((update.rowCount ?? 0) > 0) {
await insertWorkflowEvent(query, {
workflow_run_id: id,
event_type: 'workflow_failed',
data: { error },
});
}
return update;
});
} catch (dbError) {
const err = dbError as Error;
getLog().error({ err }, 'db.workflow_run_mark_failed_error');
throw new Error(`Failed to fail workflow run: ${err.message}`);
}
if (result.rowCount === 0) {
getLog().warn({ workflowRunId: id }, 'db.workflow_run_fail_no_match');
throw new Error(`Workflow run not found or already terminal (id: ${id})`);
}
}
export async function cancelWorkflowRun(
id: string,
event?: WorkflowCancellationEventDetails
): Promise<{ cancelled: boolean }> {
const dialect = getDialect();
let result: Awaited<ReturnType<IDatabase['query']>>;
try {
// Guard against re-stamping an already-finished run. Cancelling a run that
// is 'completed' or 'cancelled' must be a no-op, not a re-write of
// completed_at / a resurrection of terminal state. 'failed' is intentionally
// still cancellable (it remains a resumable state, so the user must be able
// to discard it), and a 'running' run stays cancellable — that is
// cooperative cancellation, which the executor honors via its between-layer
// status check (dag-executor).
result = await getDatabase().withTransaction(async query => {View on GitHub (pinned to 0773b97458)
Solutions
- Check the run's current status first; treat 'already terminal' as expected and skip re-failing
- Serialize failure handling so only one code path can fail a run (guard with a local flag or re-read after the error)
- If the run id may be stale, re-resolve the id from the run record before failing
- If you need to record additional error info on a terminal run, write an event/log instead of mutating status
- Retry only if you confirmed the run is in 'running' or 'pending' state
Example fix
// before
try { await failWorkflowRun(id, msg); } catch { /* retried blindly */ }
// after
try {
await failWorkflowRun(id, msg);
} catch (err) {
if (err.message.includes('already terminal') || err.message.includes('not found')) {
getLog().info({ runId: id }, 'run already terminal; skipping fail');
} else { throw err; }
} Defensive patterns
Strategy: validation
Validate before calling
const run = await getWorkflowRun(id);
if (!run) throw new Error(`run ${id} does not exist`);
if (['completed', 'failed', 'cancelled'].includes(run.status)) {
return; // already terminal: nothing to do
} Type guard
function isNonTerminal(run: { status: string } | null | undefined): boolean {
return run?.status === 'running' || run?.status === 'pending';
} Try / catch
try {
await failWorkflowRun(id, msg);
} catch (err) {
if (err.message.includes('not found or already terminal')) {
getLog().info({ runId: id }, 'run already terminal; failure not re-recorded');
} else { throw err; }
} Prevention
- Guard failure handling with a one-shot flag so only one path fails a run
- Treat 'already terminal' as success in cleanup/finally blocks
- Re-read run status after any concurrent-cancel possibility
- Do not cache run ids across database resets
- Use workflow events to append extra error detail on terminal runs instead of mutating status
When it happens
Trigger: Calling failWorkflowRun(id, ...) for: a nonexistent id; a run already 'failed' (double-failure, e.g. step failure then outer catch fails again); a run that was cancelled or completed concurrently; or a run id from a different/older database.
Common situations: Executor error handler and a finally/cleanup path both call failWorkflowRun; operator cancelled the run just before it errored; replaying failure handling after a crash against an already-terminal row; stale id cached from a previous run.
Related errors
- Failed to complete workflow run: ${err.message}
- Workflow run not found or not in running state (id: ${id})
- Failed to fail workflow run: ${err.message}
- Failed to get workflow run: ${err.message}
- Workflow run '${runId}' references codebase '${codebaseId}',
AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01).
Data as JSON: /api/errors/04d6b1b6161d8b00.
Report an issue: GitHub.