{"record":{"id":"004a96f43ac444cb","repo":"coleam00/Archon","slug":"cannot-reject-run-with-status-run-status-onl","errorCode":null,"errorMessage":"Cannot reject run with status '${run.status}'. Only paused runs can be rejected.","messagePattern":"Cannot reject run with status '(.+?)'\\. Only paused runs can be rejected\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/operations/workflow-operations.ts","lineNumber":334,"sourceCode":"          ? `Workflow run ${run.id} was already ${String(approval.resolved)} and is awaiting resume.`\n          : 'Workflow run is paused but missing approval context.'\n      );\n  }\n}\n\n/**\n * The preconditions `rejectWorkflow` enforces. Reads the same `runAttention`\n * decision as `assertApprovable` but acts on one variant differently, and that\n * difference is real: reject has no `nodeId` requirement — it falls back to\n * `approval?.nodeId ?? 'unknown'` when writing its audit event, so a run whose gate\n * metadata is unreadable (`malformed_gate`) is still legitimately rejectable. A\n * well-formed context with an unrecognized `type` is NOT one of those cases — it\n * throws, same as approve. Merging the two gates would either break reject or\n * over-permit approve.\n */\nexport function assertRejectable(run: WorkflowRun): ApprovalContext | undefined {\n  if (run.status !== 'paused') {\n    throw new Error(\n      `Cannot reject run with status '${run.status}'. Only paused runs can be rejected.`\n    );\n  }\n  const rawApproval = run.metadata.approval;\n  const approval: ApprovalContext | undefined = isApprovalContext(rawApproval)\n    ? rawApproval\n    : undefined;\n  const attention = runAttention(run);\n  switch (attention?.kind) {\n    case 'blocked_on_child':\n      // Same redirect as assertApprovable: the parent's pause is not a rejectable\n      // gate — cancelling the parent here would silently orphan the still-paused\n      // child run. Reject the child (its own gate) or abandon the parent (which\n      // cascade-cancels the subtree) instead.\n      throw new Error(\n        `Run ${run.id} is paused waiting on sub-run ${attention.childRunId} ` +\n          `('workflow:' node '${attention.nodeId}'). Reject the child run instead` +\n          `: /workflow reject ${attention.childRunId}` +","sourceCodeStart":316,"sourceCodeEnd":352,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/core/src/operations/workflow-operations.ts#L316-L352","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the run's status first (getRun/workflow status command) and only issue reject when status === 'paused'.","If the run already completed, there is nothing to reject — abandon it if you want it discarded, or start a new run.","If the run is stuck 'running' but should be stopped, cancel or abandon it instead of rejecting.","Re-fetch the run if your local view is stale; the gate may have resumed between listing and rejecting."],"exampleFix":"// before\nawait workflowRejectCommand({ runId });\n// after\nconst run = await getRun(runId);\nif (run.status === 'paused') {\n  await workflowRejectCommand({ runId });\n} else {\n  console.log(`Run is ${run.status}; nothing to reject.`);\n}","handlingStrategy":"validation","validationCode":"const run = await getRun(runId);\nif (run.status !== 'paused') throw new Error(`Run ${runId} is ${run.status}, not rejectable`);","typeGuard":"function isRejectable(run: WorkflowRun): boolean { return run.status === 'paused'; }","tryCatchPattern":"try { await workflowRejectCommand({ runId }); }\ncatch (e) {\n  if ((e as Error).message.startsWith(\"Cannot reject run with status\")) {\n    console.warn(`Skip reject: run not paused (${(e as Error).message})`);\n  } else throw e;\n}","preventionTips":["Always fetch fresh run status immediately before issuing reject.","Only surface reject actions in UI when run.status === 'paused'.","Treat pause->resume races by re-checking status after any failed reject."],"tags":["workflow","state-machine","approval-gate"],"backgroundTag":"invalid-state-transition","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}