{"record":{"id":"e5d7ae722e88aa59","repo":"coleam00/Archon","slug":"producer-failed","errorCode":"producer-failed","errorMessage":"producer-failed","messagePattern":"producer-failed","errorType":"error_code","errorClass":"OutputRefError","httpStatus":null,"severity":"error","filePath":"packages/workflows/src/output-ref.ts","lineNumber":351,"sourceCode":"  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)) {\n      throw new OutputRefError(nodeId, field, 'not-in-schema');\n    }\n    // Prefer the parsed payload; fall back to parsing the JSON-serialized output.\n    // The fallback covers older NodeOutput rows that predate `structuredOutput`,\n    // and resumes of runs persisted before `structured_output` rode along in\n    // `node_completed` events (#2637) — current resumes rehydrate the payload.\n    const obj = structuredObj ?? parseOutputObject(nodeOutput.output);\n    // No parseable object AT ALL is not a declared-optional field — it is a producer\n    // that did not honour its schema, and it must fail exactly as loudly as the","sourceCodeStart":333,"sourceCodeEnd":369,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/workflows/src/output-ref.ts#L333-L369","documentation":"A producer node whose state is `failed` never resolves a `$node.field` reference, even if its leftover output text looks like valid JSON. This is deliberate (#2713): loop_group failure paths carry the last completed iteration's real JSON, which would otherwise be silently consumed as if the group had succeeded.","triggerScenarios":"A consumer references `$producer.field` and the producer node ended in `failed` state, leaving behind a partially-written or last-iteration output string.","commonSituations":"Reading a field from a failed AI node without a retry; loop_group whose final iteration failed after earlier successful ones; missing error handling (`on_error`, fallback bindings) upstream of the consumer.","solutions":["Add retry/backoff on the producer node (`retry:`) so transient failures do not propagate.","Add an `on_error`/fallback path or `{ from, if_failed }` style handling on the consumer so it degrades explicitly.","Fix the root cause of the producer failure (inspect its error in run output).","Guard the consumer with a condition so it does not run when the producer failed."],"exampleFix":"// before\n- id: summarize\n  prompt: ...\n- id: notify\n  bash: echo \"$summarize.text\"\n// after\n- id: summarize\n  prompt: ...\n  retry: {max_attempts: 3}\n  on_error: continue\n- id: notify\n  bash: echo \"${summarize.text:-summary unavailable}\"","handlingStrategy":"try-catch","validationCode":"const o = run.nodeOutputs[producerId];\nif (o?.state === 'failed') {\n  // do not read $producer.field — leftover output is not trustworthy\n  takeFailurePath(producerId);\n}","typeGuard":"function producerSucceeded(o: { state: string } | undefined): boolean {\n  return o?.state === 'succeeded';\n}","tryCatchPattern":"try {\n  const value = resolveNodeOutputField(nodeOutput, nodeId, field);\n} catch (err) {\n  if (err instanceof OutputRefError && err.reason === 'producer-failed') {\n    log.warn(`Producer ${nodeId} failed; using fallback for '${field}'`);\n    return fallbackValue(field);\n  }\n  throw err;\n}","preventionTips":["Add `retry:` with backoff on flaky AI/bash producers so failures are absorbed upstream.","Use `on_error: continue` plus explicit fallback bindings on consumers.","Never parse a failed node's leftover output yourself — the engine blocks it deliberately (#2713).","Check node state before reading any `$node.field` in custom tooling."],"tags":["workflow","output-refs","failed-node"],"backgroundTag":"producer-node-failed","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}