coleam00/Archon · error · OutputRefError
missing-key
missing-key
Error message
missing-key
What it means
On the schemaless path, the producer's output parsed into a JSON object but does not contain the referenced key, so `$node.field` has no value. The engine throws rather than silently substituting empty, because a missing key usually means the producer dropped the field the author expects.
Source
Thrown at packages/workflows/src/output-ref.ts:401
// 2. Structured payload without a declared schema (legacy rows / non-object
// schema): prefer it, but stay lenient — with no schema we cannot tell an
// optional-absent field from a typo, so an absent field is '' (not a throw).
// A present null value is kept (callers stringify it to "null"), matching
// the historical structuredOutput-preference behavior.
if (structuredObj !== undefined) {
const value = structuredObj[field];
if (value === undefined) return { kind: 'empty' };
return { kind: 'value', value };
}
// 3. Schemaless producer (bash/script/prose). The author wrote `.field`, so
// JSON carrying that key is expected; anything else is a drop they must see.
const obj = parseOutputObject(nodeOutput.output);
if (obj === undefined) {
throw new OutputRefError(nodeId, field, unparseableReason(nodeOutput.output));
}
if (!(field in obj)) throw new OutputRefError(nodeId, field, 'missing-key');
return { kind: 'value', value: obj[field] };
}
/**
* Guard the whole-text `$node.output` form against a failed producer's stale output
* (#2696/#2710/#2713): a `loop_group`'s failure paths carry the last completed
* iteration's real, often-valid-JSON output text, which must never be read as if the
* producer had succeeded. Mirrors the `state === 'failed'` guard already built into
* `resolveNodeOutputField` above for the fielded form, so every whole-text reader
* routes through this one function instead of repeating the check (#2722), replacing
* the KEEP-IN-SYNC enumeration this module doc used to carry. This is a runtime check,
* not a type-level one — nothing stops a future caller from reading `nodeOutput.output`
* directly without calling this function first; the value is having one place to route
* through, not a compiler-enforced guarantee against bypass.
*
* `buildMessage` lets each caller keep its own wording — a binding directive names
* `if_skipped`, a `when:` guard names the condition, and so on — only the
* check-and-throw mechanism is shared.View on GitHub (pinned to 0773b97458)
Solutions
- Fix the producer so every code path emits the referenced key (with a default if needed).
- Add a shell/templating fallback at the consumer: `${producer.field:-default}` style handling or an explicit default binding.
- Declare the field in the producer's `output_format` (optional) so it is validated as consistently present-or-explicitly-null.
- Rename the consumer reference to the key the producer actually emits (log the output to check).
Example fix
// before
bash: |
if ok; then jq -n '{summary: $s}'; fi
// after
bash: |
if ok; then s=$val; else s='n/a'; fi
jq -n --arg s "$s" '{summary: $s}' Defensive patterns
Strategy: fallback
Validate before calling
const obj = parseOutputObject(nodeOutput.output);
if (obj !== undefined && !(field in obj)) {
console.warn(`Producer ${nodeId} output lacks key '${field}'; keys: ${Object.keys(obj).join(', ')}`);
} Type guard
function outputHasKey(text: string, field: string): boolean {
const obj = parseOutputObject(text);
return obj !== undefined && field in obj;
} Try / catch
try {
const { value } = resolveNodeOutputField(nodeOutput, nodeId, field);
} catch (err) {
if (err instanceof OutputRefError && err.reason === 'missing-key') {
log.warn(`$${nodeId}.${field} missing; using default`);
return defaultFor(field);
}
throw err;
} Prevention
- Make producer scripts emit every referenced key on every path, with explicit defaults.
- Declare fields in output_format (required or optional) so presence is validated at the producer.
- After changing a producer's JSON shape, grep consumers for `$producer.` refs and update them.
- Prefer `${producer.field:-default}`-style explicit fallbacks for optional fields.
When it happens
Trigger: Consumer references `$producer.field` and the producer's parsed JSON object lacks `field` — the script omitted the key on some code path, an AI node returned a differently-shaped object, or an optional field was never populated.
Common situations: Bash script whose success path prints the key but an early-exit path does not; AI model omitting an optional key despite the prompt; producer schema changed while a consumer still reads an old key name.
Related errors
- producer-not-run
- producer-failed
- not-in-schema
- unparseableReason(nodeOutput.output) (dynamic reason code fo
- Cannot execute run '${detachedPreCreatedRun.id}': it belongs
AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01).
Data as JSON: /api/errors/ddb9288ae3487e87.
Report an issue: GitHub.