coleam00/Archon · error

Cannot reject run with status '${run.status}'. Only paused r

Error message

Cannot reject run with status '${run.status}'. Only paused runs can be rejected.

What it means

assertRejectable enforces that only runs in 'paused' status can be rejected. A run is rejectable only when it is parked at a human approval gate; a running, completed, failed, or cancelled run has no gate to reject. The library throws this to prevent mutating runs whose lifecycle state does not admit a rejection decision.

Source

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

          ? `Workflow run ${run.id} was already ${String(approval.resolved)} and is awaiting resume.`
          : 'Workflow run is paused but missing approval context.'
      );
  }
}

/**
 * The preconditions `rejectWorkflow` enforces. Reads the same `runAttention`
 * decision as `assertApprovable` but acts on one variant differently, and that
 * difference is real: reject has no `nodeId` requirement — it falls back to
 * `approval?.nodeId ?? 'unknown'` when writing its audit event, so a run whose gate
 * metadata is unreadable (`malformed_gate`) is still legitimately rejectable. A
 * well-formed context with an unrecognized `type` is NOT one of those cases — it
 * throws, same as approve. Merging the two gates would either break reject or
 * over-permit approve.
 */
export function assertRejectable(run: WorkflowRun): ApprovalContext | undefined {
  if (run.status !== 'paused') {
    throw new Error(
      `Cannot reject run with status '${run.status}'. Only paused runs can be rejected.`
    );
  }
  const rawApproval = run.metadata.approval;
  const approval: ApprovalContext | undefined = isApprovalContext(rawApproval)
    ? rawApproval
    : undefined;
  const attention = runAttention(run);
  switch (attention?.kind) {
    case 'blocked_on_child':
      // Same redirect as assertApprovable: the parent's pause is not a rejectable
      // gate — cancelling the parent here would silently orphan the still-paused
      // child run. Reject the child (its own gate) or abandon the parent (which
      // cascade-cancels the subtree) instead.
      throw new Error(
        `Run ${run.id} is paused waiting on sub-run ${attention.childRunId} ` +
          `('workflow:' node '${attention.nodeId}'). Reject the child run instead` +
          `: /workflow reject ${attention.childRunId}` +

View on GitHub (pinned to 0773b97458)

Solutions

  1. Check the run's status first (getRun/workflow status command) and only issue reject when status === 'paused'.
  2. If the run already completed, there is nothing to reject — abandon it if you want it discarded, or start a new run.
  3. If the run is stuck 'running' but should be stopped, cancel or abandon it instead of rejecting.
  4. Re-fetch the run if your local view is stale; the gate may have resumed between listing and rejecting.

Example fix

// before
await workflowRejectCommand({ runId });
// after
const run = await getRun(runId);
if (run.status === 'paused') {
  await workflowRejectCommand({ runId });
} else {
  console.log(`Run is ${run.status}; nothing to reject.`);
}
Defensive patterns

Strategy: validation

Validate before calling

const run = await getRun(runId);
if (run.status !== 'paused') throw new Error(`Run ${runId} is ${run.status}, not rejectable`);

Type guard

function isRejectable(run: WorkflowRun): boolean { return run.status === 'paused'; }

Try / catch

try { await workflowRejectCommand({ runId }); }
catch (e) {
  if ((e as Error).message.startsWith("Cannot reject run with status")) {
    console.warn(`Skip reject: run not paused (${(e as Error).message})`);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling workflowRejectCommand (or the approval flow calling assertRejectable) on a run whose status is 'running', 'completed', 'failed', or 'cancelled' — e.g. rejecting a run that already finished, or a run that is still executing.

Common situations: A developer or operator runs '/workflow reject <id>' after the run already resumed and completed; a stale UI or chat message references a run ID that has since moved past its approval gate; a race where the run resumed between listing paused runs and issuing the reject.

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/004a96f43ac444cb. Report an issue: GitHub.