{"record":{"id":"b2a122875f63e72f","repo":"coleam00/Archon","slug":"producer-not-run","errorCode":"producer-not-run","errorMessage":"producer-not-run","messagePattern":"producer-not-run","errorType":"error_code","errorClass":"OutputRefError","httpStatus":null,"severity":"error","filePath":"packages/workflows/src/output-ref.ts","lineNumber":342,"sourceCode":"  }\n}\n\n/**\n * Resolve `field` against a producer's `NodeOutput`. Returns the raw field value\n * (callers stringify per their context), signals an intended empty, or throws\n * `OutputRefError` for the strict cases. See the module doc for the full table.\n */\nexport function resolveNodeOutputField(\n  nodeOutput: NodeOutput,\n  nodeId: string,\n  field: string\n): FieldResolution {\n  // A producer that did not run (skipped) or has not settled (pending) has no\n  // output to read a field from. Surface that directly rather than letting it\n  // fall through to the schemaless path and throw the misleading \"not a JSON\n  // object\" error on its empty output.\n  if (nodeOutput.state === 'skipped' || nodeOutput.state === 'pending') {\n    throw new OutputRefError(nodeId, field, 'producer-not-run');\n  }\n\n  // A failed producer never resolves a field, however JSON-shaped its leftover\n  // output looks (#2713): a loop_group's failure paths carry the last completed\n  // iteration's real, often-valid-JSON text, which would otherwise be read here\n  // as if the group had succeeded — the same class of bug #2696/#2710 fixed for\n  // the `{ from, if_skipped }` binding directive.\n  if (nodeOutput.state === 'failed') {\n    throw new OutputRefError(nodeId, field, 'producer-failed');\n  }\n\n  const declaredFields = 'declaredFields' in nodeOutput ? nodeOutput.declaredFields : undefined;\n  const structured = 'structuredOutput' in nodeOutput ? nodeOutput.structuredOutput : undefined;\n  const structuredObj = asPlainObject(structured);\n\n  // 1. Declared-schema producer — the declared property set IS the contract.\n  if (declaredFields !== undefined) {\n    if (!declaredFields.includes(field)) {","sourceCodeStart":324,"sourceCodeEnd":360,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/workflows/src/output-ref.ts#L324-L360","documentation":"resolveNodeOutputField resolves `$node.field` output references. When the producer node's state is `skipped` or `pending`, it has no output to read a field from, so the engine throws this typed OutputRefError instead of falling through to the schemaless path, which would misleadingly report 'not a JSON object' on empty output.","triggerScenarios":"A consumer node's body references `$producer.field` while the producer was skipped (an `if:` condition did not hold) or is still pending at substitution time — e.g. referencing a node downstream of a skipped branch or reading a not-yet-settled node.","commonSituations":"Branching workflows where the consumer also needs an `if:` guard so it does not read from a skipped producer; missing `if_skipped`/fallback binding directives; wiring a consumer to a producer that joins later.","solutions":["Guard the consumer with the same `if:` condition as the producer so both skip together.","Use the `{ from: nodeId, if_skipped: <value> }` binding directive (or an explicit fallback) for `$node.output` reads.","Use `$INPUTS` or a join node to supply a default when the producer may not run.","Fix workflow ordering so the producer settles before the consumer substitutes refs."],"exampleFix":"// before\n- id: consumer\n  bash: echo \"$analyze.summary\"\n// after\n- id: consumer\n  if: analyze.succeeded\n  bash: echo \"$analyze.summary\"","handlingStrategy":"fallback","validationCode":"// before scheduling the consumer, check producer state\nconst state = run.nodeOutputs[producerId]?.state;\nif (state === 'skipped' || state === 'pending') {\n  skipConsumer(producerId); // or attach the same `if:` condition\n}","typeGuard":"function producerRan(o: { state: string }): o is { state: 'succeeded' | 'failed' } {\n  return o.state === 'succeeded' || o.state === 'failed';\n}","tryCatchPattern":"try {\n  const value = resolveNodeOutputField(nodeOutput, nodeId, field);\n} catch (err) {\n  if (err instanceof OutputRefError && err.reason === 'producer-not-run') {\n    return defaultFor(field); // explicit, logged fallback\n  }\n  throw err;\n}","preventionTips":["Give consumers that depend on a conditional producer the same `if:` condition.","Use `{ from, if_skipped }`/fallback bindings instead of raw `$node.field` when a producer may be skipped.","Do not reference nodes that run later in the topology.","Review the run graph so every `$node.field` edge has a producer that always executes."],"tags":["workflow","output-refs","skipped-node"],"backgroundTag":"producer-node-not-run","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}