{"record":{"id":"a082bcd1efea87cc","repo":"coleam00/Archon","slug":"run-run-id-s-gate-approval-type-only-acc","errorCode":null,"errorMessage":"Run ${run.id}'s gate ('${approval.type}') only accepts 'approve' or 'reject' — '${decision}' is not a valid response here.","messagePattern":"Run (.+?)'s gate \\('(.+?)'\\) only accepts 'approve' or 'reject' — '(.+?)' is not a valid response here\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/operations/workflow-operations.ts","lineNumber":896,"sourceCode":"}\n\n/**\n * Validate that `decision` is legal for the gate `run` is paused at, WITHOUT\n * mutating anything (mirrors `assertApprovable`'s read-only-precheck role for\n * CLI `--detach`). `approve`/`reject` are always legal (delegated to the\n * existing `approveWorkflow`/`rejectWorkflow` machinery by `respondToWorkflow`\n * — this function is not consulted for those two ids). Any OTHER decision is\n * legal only for a plain gate node (`approval`/`undefined` suspend type) whose\n * author explicitly declared `approval.decisions:` (`decisionsAuthored`) —\n * `writeback`/`interactive_loop`/`child_workflow` pauses have no author-\n * declared vocabulary and accept only approve/reject; a legacy gate (no\n * `decisions:` authored) also only ever has the synthesized approve/reject\n * 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;","sourceCodeStart":878,"sourceCodeEnd":914,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/core/src/operations/workflow-operations.ts#L878-L914","documentation":"assertRespondable validates a human response to a workflow run's gate. This error means the run's gate node is not an 'approval' (or untyped) gate — e.g. it is a different gate type that does not accept simple approve/reject semantics. The engine refuses the binary decision because the gate's type defines a different response contract.","triggerScenarios":"Calling workflowRespondCommand, the 'approval' operation, or the API route POST respond with decision 'approve' or 'reject' against a run whose current gate node has approval.type set to something other than 'approval' (or undefined).","commonSituations":"A workflow was edited to use a custom gate type but an old bot command, Slack button, or CLI script still sends 'approve'; an automated integration hardcodes approve/reject for all gates; a run resolved a different gate type than the responder expected.","solutions":["Check the run's current gate node and confirm its approval.type; use the response vocabulary that gate type actually accepts.","If the gate should be a plain approval, change the workflow node so approval.type is 'approval' (or unset).","If the gate intentionally declares a broader vocabulary, author approval.decisions on the node and respond with one of those decision ids instead.","Update callers (CLI/scripts/platform handlers) to inspect the gate type via the run status API before responding."],"exampleFix":"// before\nawait respondToWorkflow(runId, 'approve');\n// after\nconst run = await getWorkflowRun(runId);\nif (run.gate?.type === 'approval' || run.gate?.type === undefined) {\n  await respondToWorkflow(runId, 'approve');\n} else {\n  await respondToWorkflow(runId, 'request_changes'); // a declared decision\n}","handlingStrategy":"validation","validationCode":"const gate = run.currentGate;\nif (gate && gate.type !== 'approval' && gate.type !== undefined) {\n  throw new Error(`Gate type '${gate.type}' does not accept approve/reject`);\n}","typeGuard":"function isPlainApprovalGate(gate) {\n  return gate === undefined || gate.type === undefined || gate.type === 'approval';\n}","tryCatchPattern":"try {\n  await respondToWorkflow(runId, decision);\n} catch (e) {\n  if (e.message.includes(\"only accepts 'approve' or 'reject'\")) {\n    const run = await getWorkflowRun(runId);\n    // inspect run.gate.type and re-route the response\n  }\n  throw e;\n}","preventionTips":["Fetch run status and inspect the gate type before sending any decision.","Keep platform button handlers typed per gate type, not a universal approve/reject.","Declare approval.decisions when you need custom response vocabulary."],"tags":["workflow","gate","validation","api"],"backgroundTag":"invalid-gate-decision","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}