coleam00/Archon · warning

Workflow run ${run.id} was already ${String(approval.resolve

Error message

Workflow run ${run.id} was already ${String(approval.resolved)} and is awaiting resume.

What it means

When the run has no attention block pointer but its approval metadata already carries a resolved decision (isGateResolved), the gate was already decided and the run is merely waiting for the engine to resume it. Rejecting again would double-resolve the gate, so the library throws.

Source

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

      // 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}` +
          ' To discard the whole tree, abandon this run.'
      );
    case 'unreadable':
      // The one deliberate divergence from approve: unreadable gate METADATA is
      // still rejectable (see this function's doc comment). An unrecognized gate
      // TYPE is not, and neither is a block pointer with nothing to follow.
      if (attention.reason === 'malformed_gate') break;
      throw new Error(unreadableGateMessage(run, attention, approval));
    case undefined:
      if (approval && isGateResolved(approval)) {
        throw new Error(
          `Workflow run ${run.id} was already ${String(approval.resolved)} and is awaiting resume.`
        );
      }
      break;
    case 'awaiting_response':
    case 'terminal':
      // 'terminal' is unreachable: the status guard above already returned for it.
      break;
  }
  return approval;
}

// ---------------------------------------------------------------------------
// Operations
// ---------------------------------------------------------------------------

/**
 * List all running and paused workflow runs.

View on GitHub (pinned to 0773b97458)

Solutions

  1. Do nothing — the gate is already resolved; wait for the run to resume on its own.
  2. If the run stays paused indefinitely, resume it manually with the resume command or investigate the resume worker.
  3. Debounce/deduplicate decision commands per run ID in your UI or bot layer.

Example fix

// before
await workflowRejectCommand({ runId });
// after
if (!isGateResolved(run.metadata.approval)) {
  await workflowRejectCommand({ runId });
} // else: already decided, awaiting resume
Defensive patterns

Strategy: type-guard

Validate before calling

if (isGateResolved(run.metadata?.approval)) return; // already decided, awaiting resume

Type guard

function isGateResolved(a: unknown): boolean {
  return !!a && typeof a === 'object' && 'resolved' in a && (a as any).resolved != null;
}

Try / catch

try { await workflowRejectCommand({ runId }); }
catch (e) {
  if ((e as Error).message.includes('already') && (e as Error).message.includes('awaiting resume')) return; // no-op
  throw e;
}

Prevention

When it happens

Trigger: Calling workflowRejectCommand on a paused run where attention is undefined and approval.metadata passes isGateResolved — i.e. approve/reject already committed but the resume hasn't executed yet.

Common situations: Double-clicking or re-sending a reject command; a bot retries a rejected command after a timeout while the resume is still queued; UI state not refreshed after the first successful decision.

Related errors


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