coleam00/Archon · error

Failed to abandon workflow run ${run.id}: ${err.message}

Error message

Failed to abandon workflow run ${run.id}: ${err.message}

What it means

cancelRunAndCleanup wraps the underlying cancellation of a run during abandon; if the cancel call throws for any reason, the error is logged (with type and runId under 'operations.workflow_abandon_failed') and rethrown as 'Failed to abandon workflow run <id>: <cause>'. This is a boundary translation that preserves the original message for diagnostics.

Source

Thrown at packages/core/src/operations/workflow-operations.ts:440

interface AbandonAttemptResult extends AbandonWorkflowResult {
  cancelledDescendants: number;
}

async function cancelRunAndCleanup(
  run: WorkflowRun,
  cancelRun: CancelWorkflowRun
): Promise<AbandonAttemptResult> {
  let cancelled: boolean;
  try {
    ({ cancelled } = await cancelRun(run.id));
  } catch (error) {
    const err = error as Error;
    getLog().error(
      { err, errorType: err.constructor.name, runId: run.id },
      'operations.workflow_abandon_failed'
    );
    throw new Error(`Failed to abandon workflow run ${run.id}: ${err.message}`);
  }

  // The same cancellation policy applies to descendants. This keeps `/reset`'s
  // resumable-only ownership boundary intact through the complete run tree.
  let cascadeFailures = 0;
  let cancelledDescendants = 0;
  if (cancelled) {
    ({ cancelled: cancelledDescendants, failures: cascadeFailures } = await cascadeCancelChildren(
      run.id,
      cancelRun
    ));
  }
  const blockedParentRunId = cancelled ? await findParentBlockedOn(run) : null;

  // Reclaim only when our cancel won the CAS. A miss means another lifecycle
  // owner now controls the run and its environment.
  if (cancelled) await reclaimCancelledRunContainer(run);
  return { run, cancelled, cancelledDescendants, cascadeFailures, blockedParentRunId };

View on GitHub (pinned to 0773b97458)

Solutions

  1. Read the underlying cause in err.message and the 'operations.workflow_abandon_failed' log entry to fix the root issue (DB connectivity, lock, disk).
  2. Retry the abandon once the database issue is resolved — abandon is safe to re-attempt on a still-abandonable run.
  3. Verify no concurrent process is mutating the same run (check run status before retrying).
  4. If the DB is persistently failing, check database health/permissions before further operations.

Example fix

// before
await abandonWorkflow(runId); // may throw raw DB error wrapped here
// after
try {
  await abandonWorkflow(runId);
} catch (e) {
  logger.error({ runId, cause: (e as Error).message });
  // fix DB issue, then retry
}
Defensive patterns

Strategy: try-catch

Validate before calling

await db.ping(); // verify database reachability before bulk abandon operations

Try / catch

try { await abandonWorkflow(runId); }
catch (e) {
  const msg = (e as Error).message;
  if (msg.startsWith('Failed to abandon workflow run')) {
    logger.error({ runId, cause: msg }, 'abandon failed; inspect operations.workflow_abandon_failed log and retry after fixing DB');
  } else throw e;
}

Prevention

When it happens

Trigger: workflowDb.cancelWorkflowRun (or the injected cancel function) throws while abandoning a run — e.g. database write failure, lock contention, or a concurrent modification of the run row.

Common situations: SQLite/Postgres connection loss or busy-lock during abandon; two operators abandoning the same run simultaneously; disk-full or migration mismatch preventing the status write.

Related errors


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