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

  1. Verify only one Archon process/worker is running against this database before resuming waits
  2. 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
  3. Re-run resume from a fresh workflow run rather than re-driving the suspended run
  4. 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

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


AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01). Data as JSON: /api/errors/75053735af3496d8. Report an issue: GitHub.