{"record":{"id":"ba7ea2b8169096a5","repo":"coleam00/Archon","slug":"run-run-id-s-gate-does-not-declare-decision","errorCode":null,"errorMessage":"Run ${run.id}'s gate does not declare decision '${decision}'. Declared decisions: ${declaredIds.join(', ')}.","messagePattern":"Run (.+?)'s gate does not declare decision '(.+?)'\\. Declared decisions: (.+?)\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/operations/workflow-operations.ts","lineNumber":909,"sourceCode":" * pair (#2707 step 2).\n */\nexport function assertRespondable(run: WorkflowRun, decision: string): ApprovalContext {\n  const approval = assertApprovable(run);\n  if (approval.type !== 'approval' && approval.type !== undefined) {\n    throw new Error(\n      `Run ${run.id}'s gate ('${approval.type}') only accepts 'approve' or 'reject' — ` +\n        `'${decision}' is not a valid response here.`\n    );\n  }\n  if (approval.decisionsAuthored !== true) {\n    throw new Error(\n      `Run ${run.id}'s gate only accepts 'approve' or 'reject' — '${decision}' is not one of its ` +\n        'declared decisions. Declare `approval.decisions:` on the gate node to author a broader vocabulary.'\n    );\n  }\n  const declaredIds = (approval.decisions ?? []).map(d => d.id);\n  if (!declaredIds.includes(decision)) {\n    throw new Error(\n      `Run ${run.id}'s gate does not declare decision '${decision}'. Declared decisions: ` +\n        `${declaredIds.join(', ')}.`\n    );\n  }\n  return approval;\n}\n\n/**\n * Resolve a paused gate with an author-declared decision beyond approve/reject\n * (#2707 step 2 — the general `workflow respond <id> <decision> [text]` verb).\n * `approve`/`reject` are NOT handled here — `respondToWorkflow` delegates those\n * to the existing `approveWorkflow`/`rejectWorkflow` functions unchanged, so\n * every gate shape that existed before this PR keeps its exact prior behavior\n * (legacy `on_reject` rework/cancel, `capture_response`, interactive_loop,\n * writeback). This function only ever resolves a new-mode plain gate node\n * (`decisionsAuthored: true`) immediately with structured `{decision, text}`\n * output — the same shape `approveWorkflow`'s new-mode branch writes, just\n * with a caller-supplied `decision` instead of the literal `'approve'`.","sourceCodeStart":891,"sourceCodeEnd":927,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/core/src/operations/workflow-operations.ts#L891-L927","documentation":"The gate does declare a custom decisions vocabulary, but the submitted decision id is not among the declared ids. assertRespondable checks the submitted decision against approval.decisions and rejects anything not listed.","triggerScenarios":"Calling respondToWorkflow / workflowRespondCommand / the respond API route with a decision string that is not in the gate node's `approval.decisions:` id list — e.g. typo 'approve ' (trailing space), 'Accept', or an id renamed in the workflow after the run started.","commonSituations":"Workflow decisions renamed between authoring and running, so in-flight runs declare old ids; case or whitespace mismatch in decision ids; platform handlers forwarding button values that don't match declared ids.","solutions":["Read the run's declared decisions (the error lists them) and send one of those exact ids.","Fix typos/casing/whitespace in the decision id at the caller.","If a new decision is genuinely needed, add it to `approval.decisions:` in the workflow for future runs (already-running runs keep their declared set)."],"exampleFix":"// before\nawait respondToWorkflow(runId, 'Request_Changes');\n// after\nconst declared = ['approve', 'reject', 'request_changes'];\nawait respondToWorkflow(runId, 'request_changes'); // exact declared id","handlingStrategy":"validation","validationCode":"const declaredIds = (gate.decisions ?? []).map(d => d.id);\nif (!declaredIds.includes(decision)) {\n  throw new Error(`'${decision}' not declared. Declared: ${declaredIds.join(', ')}`);\n}","typeGuard":"function isDeclaredDecision(decision, gate) {\n  return (gate.decisions ?? []).some(d => d.id === decision);\n}","tryCatchPattern":"try {\n  await respondToWorkflow(runId, decision);\n} catch (e) {\n  const m = e.message.match(/Declared decisions: (.+)\\./);\n  if (m) console.error(`Use one of: ${m[1]}`);\n  throw e;\n}","preventionTips":["Load declared decisions from the run API instead of hardcoding ids in handlers.","Avoid renaming decision ids while runs are in flight; add new ids instead.","Trim and lowercase-normalize decision ids at the UI boundary before sending."],"tags":["workflow","gate","decisions","validation"],"backgroundTag":"invalid-gate-decision","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}