coleam00/Archon · warning
node_on_reject_deprecated
node_on_reject_deprecated
Error message
Node '${id}': 'approval.on_reject' is deprecated. Declare 'approval.decisions' and wire a rework node with "when: \"$${id}.output.decision == 'reject'\"" instead (loop it with loop_group if it should iterate). This gate keeps running via the legacy mechanism until migrated. What it means
The `approval.on_reject` hook is the legacy mechanism for handling a rejected gate. The current model declares possible outcomes with `approval.decisions` and wires rejection as an ordinary node gated with `when:` on the structured output, optionally looped with loop_group. The loader warns that on_reject still runs via the legacy mechanism until migrated.
Source
Thrown at packages/workflows/src/loader.ts:459
// no 'decisions:' authored, it still fully controls whether the
// reviewer's comment becomes the node's output, exactly as before this
// PR. Warning unconditionally would be false in those cases.
if (approvalObj.capture_response !== undefined && node.decisionsAuthored) {
const message =
`Node '${id}': 'approval.capture_response' is deprecated. Gate output is now ` +
`always structured as {decision, text} — read '$${id}.output.text' downstream ` +
'instead. This field is ignored.';
warnings.push(message);
getLog().warn({ id, warning: message }, 'node_capture_response_deprecated');
}
if (approvalObj.on_reject !== undefined) {
const message =
`Node '${id}': 'approval.on_reject' is deprecated. Declare 'approval.decisions' ` +
`and wire a rework node with "when: \\"$${id}.output.decision == 'reject'\\"" ` +
'instead (loop it with loop_group if it should iterate). This gate keeps running ' +
'via the legacy mechanism until migrated.';
warnings.push(message);
getLog().warn({ id, warning: message }, 'node_on_reject_deprecated');
}
}
}
const interactiveLoop =
(isLoopNode(node) && node.loop.interactive === true) ||
(isLoopGroupNode(node) && node.loop_group.interactive === true);
if (interactiveLoop) {
const message =
`Node '${id}': node-level loop 'interactive:' is deprecated. A future release ` +
're-expresses the interactive loop as a gate + loop_group composition (#2707 step 3). ' +
'Continue using it for now.';
warnings.push(message);
getLog().warn({ id, warning: message }, 'node_loop_interactive_deprecated');
}
// The prose `until:` completion channel is deprecated for EVERY loop/loop_group,
// not only interactive ones (#2707 step 3, "What gets deleted"): its one stated
// reason to exist — "the iteration output is a message a human reads at anView on GitHub (pinned to 0773b97458)
Solutions
- Declare the gate's outcomes with `approval.decisions` (e.g. [approve, reject]).
- Remove `on_reject` and wire the rework node with `when: "$<gateId>.output.decision == 'reject'"`, wrapping it in loop_group if the gate should repeat after rework.
- Test the reject path after migrating — the legacy mechanism is temporary.
- Sweep the workflow library and migrate every on_reject before the legacy path is deleted.
Example fix
# before
approval:
prompt: "Ship?"
on_reject:
node: fix_issues
# after
approval:
prompt: "Ship?"
decisions: [approve, reject]
nodes:
fix_issues:
when: "$approve.output.decision == 'reject'" Defensive patterns
Strategy: validation
Validate before calling
// Flag legacy on_reject hooks at author time:
for (const node of workflow.nodes) {
if (node.type === 'approval' && node.approval?.on_reject !== undefined) {
throw new Error(`Node '${node.id}': migrate approval.on_reject to approval.decisions + a when:-gated rework node`);
}
} Type guard
function usesOnReject(node: { type: string; approval?: Record<string, unknown> }): boolean {
return node.type === 'approval' && node.approval?.on_reject !== undefined;
} Prevention
- Always declare approval.decisions on gates instead of on_reject hooks.
- Express rejection handling as ordinary nodes gated with when: on $<gateId>.output.decision.
- Use loop_group when a gate should repeat after a rework cycle.
- Test the reject path after migrating; the legacy mechanism will eventually be deleted.
When it happens
Trigger: collectGateAndLoopDeprecationWarnings (via parseDagNode) on a workflow where an approval node defines `approval.on_reject`; fires at parse time on every load of the file (loader.ts:459).
Common situations: Workflows authored before `approval.decisions` existed; copied templates using on_reject; authors wanting rework-after-reject who don't yet know the when:-based pattern.
Related errors
- ⚠️ `${workflow.name}` is deprecated and will be removed in a
- node_capture_response_deprecated
- Cannot reject run with status '${run.status}'. Only paused r
- Run ${run.id} is paused waiting on sub-run ${attention.child
- unreadableGateMessage(run, attention, approval) — one of: "R
AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01).
Data as JSON: /api/errors/b3258d41f9f1e9d8.
Report an issue: GitHub.