{"record":{"id":"55c1a352ec081e9c","repo":"coleam00/Archon","slug":"not-in-schema","errorCode":"not-in-schema","errorMessage":"not-in-schema","messagePattern":"not-in-schema","errorType":"error_code","errorClass":"OutputRefError","httpStatus":null,"severity":"error","filePath":"packages/workflows/src/output-ref.ts","lineNumber":361,"sourceCode":"  }\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\n    // schemaless path below (#2456). Returning empty here made declaring\n    // `output_format` QUIETER than declaring nothing, which is backwards: a\n    // `workflow:` node's output_format is never validated against the child (it only\n    // populates declaredFields), so every declared field silently became ''.\n    if (obj === undefined) {\n      throw new OutputRefError(nodeId, field, unparseableReason(nodeOutput.output));\n    }\n    const value = obj[field];\n    // Required fields are guaranteed present (the producer validated post-parse),\n    // so a missing/explicit-null value here is a declared-optional field → empty.","sourceCodeStart":343,"sourceCodeEnd":379,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/workflows/src/output-ref.ts#L343-L379","documentation":"For a producer that declares an output schema (`output_format` with a `properties` map), the declared field set IS the contract: referencing a field not in the declared schema throws immediately. This catches the author early — the referenced field can never appear, even if the producer emits extra keys.","triggerScenarios":"A consumer references `$producer.fieldName` where producer declares `output_format.properties` without `fieldName` (typo, renamed schema property, schema tightened after the consumer was written).","commonSituations":"Renaming a property in the producer's output_format without updating consumers; AI node schema updated to a subset; copied consumer code referencing fields from a different producer.","solutions":["Add `fieldName` to the producer's `output_format.properties` if the producer should emit it.","Or correct the consumer's `$producer.field` reference to a declared field name.","Or drop the `output_format` declaration (making the producer schemaless) only if untyped access is truly intended — this loses validation."],"exampleFix":"// before\noutput_format:\n  properties:\n    summary: {type: string}\n// after\noutput_format:\n  properties:\n    summary: {type: string}\n    score: {type: number}","handlingStrategy":"validation","validationCode":"function assertFieldDeclared(producer: { output_format?: Record<string, unknown> }, field: string): void {\n  const props = producer.output_format?.properties as Record<string, unknown> | undefined;\n  if (props && !(field in props)) {\n    throw new Error(`$producer.${field} is not declared in output_format properties: ${Object.keys(props).join(', ')}`);\n  }\n}","typeGuard":"function isDeclaredField(schema: { properties?: Record<string, unknown> }, field: string): field is keyof schema['properties'] {\n  return schema.properties !== undefined && field in schema.properties;\n}","tryCatchPattern":"try {\n  const { value } = resolveNodeOutputField(nodeOutput, nodeId, field);\n} catch (err) {\n  if (err instanceof OutputRefError && err.reason === 'not-in-schema') {\n    console.error(`${field} missing from producer's declared output_format; fix schema or reference`);\n  } else throw err;\n}","preventionTips":["Change producer schema and consumer references in the same commit; grep for `$producer.` refs.","Keep output_format.properties as the single source of truth for consumer-facing fields.","Add a lint that cross-checks `$node.field` refs against producers' declared fields.","Avoid adding consumer refs to fields the producer only emits informally."],"tags":["workflow","output-refs","schema"],"backgroundTag":"field-not-in-output-schema","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}