coleam00/Archon · error

Cannot abandon run with status '${run.status}'. Only running

Error message

Cannot abandon run with status '${run.status}'. Only running, paused, or failed runs can be abandoned.

What it means

abandonWorkflow intentionally diverges from the resumable-status constant: it blocks only the two terminal states 'completed' and 'cancelled', since those runs are already finished and cannot be discarded. Running, paused, and failed runs can be abandoned to cancel them and cascade-cancel descendants.

Source

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

  // 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 };
}

/**
 * Abandon a workflow run (marks it as cancelled).
 *
 * Running, paused, AND failed runs can be abandoned. A `failed` run is terminal
 * per TERMINAL_WORKFLOW_STATUSES but remains resumable, so the user must be able
 * to discard it — hence the inline check here intentionally diverges from that
 * constant and blocks only the two non-resumable terminal states.
 */
export async function abandonWorkflow(runId: string): Promise<AbandonWorkflowResult> {
  const run = await getRunOrThrow(runId, 'operations.workflow_abandon_lookup_failed');
  if (run.status === 'completed' || run.status === 'cancelled') {
    throw new Error(
      `Cannot abandon run with status '${run.status}'. Only running, paused, or failed runs can be abandoned.`
    );
  }
  const result = await cancelRunAndCleanup(run, workflowDb.cancelWorkflowRun);
  return {
    run: result.run,
    cancelled: result.cancelled,
    cascadeFailures: result.cascadeFailures,
    blockedParentRunId: result.blockedParentRunId,
  };
}

export interface AbandonConversationRunsResult {
  /** Runs this call actually took to 'cancelled'. */
  abandoned: number;
  /**
   * First cancelled run that left a parent outside the conversation-scoped
   * mutation paused blocked-on-child (stranded parent id), or null. The user

View on GitHub (pinned to 0773b97458)

Solutions

  1. Check status before abandoning: only call abandonWorkflow for 'running', 'paused', or 'failed' runs.
  2. Treat 'completed' runs as permanent history — archive or ignore them rather than discarding.
  3. If you get this on 'cancelled', the abandon already happened; no action needed.
  4. Filter cleanup jobs to non-terminal statuses via the run listing query.

Example fix

// before
for (const run of runs) await abandonWorkflow(run.id);
// after
for (const run of runs) {
  if (run.status !== 'completed' && run.status !== 'cancelled') {
    await abandonWorkflow(run.id);
  }
}
Defensive patterns

Strategy: validation

Validate before calling

if (run.status === 'completed' || run.status === 'cancelled') skip; else await abandonWorkflow(run.id);

Type guard

function isAbandonable(run: WorkflowRun): boolean {
  return run.status !== 'completed' && run.status !== 'cancelled';
}

Try / catch

try { await abandonWorkflow(runId); }
catch (e) {
  if ((e as Error).message.startsWith("Cannot abandon run with status")) {
    console.info('Run already terminal; nothing to abandon.');
  } else throw e;
}

Prevention

When it happens

Trigger: Calling abandonWorkflow on a run whose status is 'completed' or 'cancelled'.

Common situations: A cleanup script sweeping old runs hits already-completed ones; an operator double-abandons a run that was already cancelled; automation retries abandon after a first success.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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