coleam00/Archon · error · Error
Wait node '${node.id}' lost ownership of its persisted wait
Error message
Wait node '${node.id}' lost ownership of its persisted wait cursor What it means
The DAG executor persists a 'wait cursor' when a wait node suspends so a later resume can be attributed to the same owner. When completing or expiring the wait, clearWorkflowWaitContext must report the cursor was cleared; if it returns cleared:false, the persisted cursor no longer matches this run/node, so the executor refuses to proceed rather than double-resume a wait someone else already consumed.
Source
Thrown at packages/workflows/src/dag-executor.ts:7206
const result = {
status,
waited_ms: Math.max(0, now.getTime() - Date.parse(context.waitingSince)),
...(context.kind === 'event'
? {
event: context.event,
...(context.payload !== undefined ? { payload: context.payload } : {}),
}
: {}),
} as const;
const output = JSON.stringify(result);
const stepName = stepNamePrefix + node.id;
if (persisted !== undefined) {
const { cleared } = await deps.store.clearWorkflowWaitContext(workflowRun.id, context, {
stepName,
result,
});
if (!cleared) {
throw new Error(`Wait node '${node.id}' lost ownership of its persisted wait cursor`);
}
} else {
await deps.store.createWorkflowEvent({
workflow_run_id: workflowRun.id,
event_type: status === 'expired' ? 'wait_expired' : 'wait_completed',
step_name: stepName,
data: result,
});
await deps.store.createWorkflowEvent({
workflow_run_id: workflowRun.id,
event_type: 'node_completed',
step_name: stepName,
data: {
type: 'wait',
duration_ms: result.waited_ms,
node_output: output,
structured_output: result,
},View on GitHub (pinned to 0773b97458)
Solutions
- Verify only one Archon process/worker is running against this database before resuming waits
- Check workflow_events for a prior wait_completed/wait_expired event for this step to confirm it was already resumed; treat the run as done instead of retrying
- Re-run resume from a fresh workflow run rather than re-driving the suspended run
- Inspect clearWorkflowWaitContext in the store adapter to confirm cursor ownership (run id + step) matches before resuming
Example fix
// before: blindly retrying the resume
await engine.resume(runId, stepName, result);
// after: check prior completion first
const events = await store.getWorkflowEvents(runId);
if (!events.some(e => e.step_name === stepName && ['wait_completed','wait_expired'].includes(e.event_type))) {
await engine.resume(runId, stepName, result);
} Defensive patterns
Strategy: try-catch
Validate before calling
const events = await store.getWorkflowEvents(runId);
if (events.some(e => e.step_name === stepName && ['wait_completed','wait_expired'].includes(e.event_type))) {
throw new Error('wait already resolved; do not resume again');
} Type guard
function canResumeWait(events, stepName: string): boolean {
return !events.some(e => e.step_name === stepName && ['wait_completed','wait_expired'].includes(e.event_type));
} Try / catch
try {
await engine.resume(runId, stepName, result);
} catch (err) {
if (err instanceof Error && err.message.includes('lost ownership of its persisted wait cursor')) {
// treat as already-resumed; inspect events instead of retrying
} else throw err;
} Prevention
- Ensure a single Archon process owns the database (no concurrent installs on one DSN)
- Check for prior wait_completed/wait_expired events before issuing a resume
- Avoid manual retries of resume calls after a timeout; verify run state first
When it happens
Trigger: A wait node resumes while another process or an earlier resume already cleared the persisted wait context for the same workflow run and step; concurrent resume attempts or a duplicated wait-expiration callback make the second clearWorkflowWaitContext return cleared:false.
Common situations: Running two Archon instances against the same SQLite/Postgres database; a retry after a partially-completed resume (event written, cursor already cleared); operator manually resuming an expired wait that was already processed.
Related errors
- Run config key 'assistants.pi.maxConcurrent' cannot apply: P
- Resumable run snapshot changed during reset (expected ${Stri
- Failed to claim write-back apply: ${err.message}
- Workflow run ${runId} was already resolved and is awaiting r
- workflow.fan_out_shared_checkout_collision
AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01).
Data as JSON: /api/errors/75053735af3496d8.
Report an issue: GitHub.