coleam00/Archon · error
Node '${consumerId}' binding '${name}': an object value must
Error message
Node '${consumerId}' binding '${name}': an object value must be a binding directive { from: '$node.output[.field]', if_skipped?: <value> }. Use a string, number, boolean, null, or array for a literal value. What it means
resolveNodeBindings accepts either a plain literal value or an explicit binding directive object { from, if_skipped? } for each with: entry. A plain object that is neither (a binding directive shape was not recognized) is rejected. The YAML loader normally rejects this at load time, but programmatic workflow definitions can reach the executor, so the failure is kept loud and actionable.
Source
Thrown at packages/workflows/src/dag-executor.ts:456
* for that simulator only; it must resolve bindings with THIS function so preview
* and execution cannot drift (#2637 R2).
*/
export function resolveNodeBindings(
consumerId: string,
withMap: Record<string, JsonValue | BindingDirective>,
ctx: ShellInputContext,
runInputs: Record<string, JsonValue> | undefined
): Record<string, JsonValue> {
const resolved: Record<string, JsonValue> = {};
for (const [name, rawValue] of Object.entries(withMap)) {
if (isBindingDirective(rawValue)) {
resolved[name] = resolveBindingDirective(consumerId, name, rawValue, ctx);
continue;
}
if (typeof rawValue === 'object' && rawValue !== null && !Array.isArray(rawValue)) {
// The loader rejects this shape at load time; programmatic definitions can
// still reach here, so keep the failure loud and actionable.
throw new Error(
`Node '${consumerId}' binding '${name}': an object value must be a binding directive ` +
"{ from: '$node.output[.field]', if_skipped?: <value> }. Use a string, number, " +
'boolean, null, or array for a literal value.'
);
}
resolved[name] = resolveWorkflowValue(rawValue, ctx, runInputs, true);
}
return resolved;
}
function resolveBindingDirective(
consumerId: string,
name: string,
directive: BindingDirective,
ctx: ShellInputContext
): JsonValue {
const ref = parseWholeOutputRef(directive.from);
if (ref === undefined) {View on GitHub (pinned to 0773b97458)
Solutions
- Wrap the object in a binding directive: { from: '$node.output[.field]', if_skipped?: <value> }.
- Replace the object with a literal (string, number, boolean, null, or array) if a static value is intended.
- Validate the definition through the loader before execution so the error surfaces at load time with richer context.
Example fix
// before
with:
config:
model: fast
prompt: hello
// after
with:
config: '{"model":"fast","prompt":"hello"}' # literal, or:
config:
from: '$build_config.output' Defensive patterns
Strategy: validation
Validate before calling
function isBindingDirective(v: unknown): boolean {
return typeof v === 'object' && v !== null && !Array.isArray(v) && 'from' in v;
}
function validateWithValue(v: unknown): string | null {
if (typeof v === 'object' && v !== null && !Array.isArray(v) && !isBindingDirective(v))
return 'Object with: values must be { from: ... } directives or literals';
return null;
} Type guard
function isBindingDirective(v: unknown): v is { from: string; if_skipped?: unknown } {
return typeof v === 'object' && v !== null && !Array.isArray(v) && typeof (v as any).from === 'string';
} Try / catch
try {
runWorkflow(wf);
} catch (e) {
if (e instanceof Error && e.message.includes('must be a binding directive')) {
console.error('Invalid with: value shape — use { from } or a literal:', e.message);
} else throw e;
} Prevention
- Run every definition through the loader before execution — it rejects this shape with better context.
- Serialize nested config objects to JSON strings when a literal object payload is needed.
- Add a type on programmatic builders so with: values are JsonValue | BindingDirective, not any.
When it happens
Trigger: Passing a Record/object as a with: value through a programmatically constructed workflow definition (API or code that builds the withMap) where the object lacks the recognized 'from' key or directive shape.
Common situations: Building workflow JSON in TypeScript and nesting a config object (e.g. { model: ..., prompt: ... }) as a binding value expecting it to pass through; typos like 'from_' or 'source:' instead of 'from:'; loader bypass in tests or the dry-run simulator path.
Related errors
- Node '${consumerId}' binding '${name}': 'from' must be exact
- Invalid --status '${opts.status}'. Valid: ${workflowRunStatu
- Cannot signal a non-event workflow wait
- Run ${run.id}'s gate ('${approval.type}') only accepts 'appr
- Run ${run.id}'s gate only accepts 'approve' or 'reject' — '$
AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01).
Data as JSON: /api/errors/24b61e1f8e7ffbdb.
Report an issue: GitHub.