coleam00/Archon · error · OutputRefError

not-in-schema

not-in-schema

Error message

not-in-schema

What it means

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.

Source

Thrown at packages/workflows/src/output-ref.ts:361

  }

  // A failed producer never resolves a field, however JSON-shaped its leftover
  // output looks (#2713): a loop_group's failure paths carry the last completed
  // iteration's real, often-valid-JSON text, which would otherwise be read here
  // as if the group had succeeded — the same class of bug #2696/#2710 fixed for
  // the `{ from, if_skipped }` binding directive.
  if (nodeOutput.state === 'failed') {
    throw new OutputRefError(nodeId, field, 'producer-failed');
  }

  const declaredFields = 'declaredFields' in nodeOutput ? nodeOutput.declaredFields : undefined;
  const structured = 'structuredOutput' in nodeOutput ? nodeOutput.structuredOutput : undefined;
  const structuredObj = asPlainObject(structured);

  // 1. Declared-schema producer — the declared property set IS the contract.
  if (declaredFields !== undefined) {
    if (!declaredFields.includes(field)) {
      throw new OutputRefError(nodeId, field, 'not-in-schema');
    }
    // Prefer the parsed payload; fall back to parsing the JSON-serialized output.
    // The fallback covers older NodeOutput rows that predate `structuredOutput`,
    // and resumes of runs persisted before `structured_output` rode along in
    // `node_completed` events (#2637) — current resumes rehydrate the payload.
    const obj = structuredObj ?? parseOutputObject(nodeOutput.output);
    // No parseable object AT ALL is not a declared-optional field — it is a producer
    // that did not honour its schema, and it must fail exactly as loudly as the
    // schemaless path below (#2456). Returning empty here made declaring
    // `output_format` QUIETER than declaring nothing, which is backwards: a
    // `workflow:` node's output_format is never validated against the child (it only
    // populates declaredFields), so every declared field silently became ''.
    if (obj === undefined) {
      throw new OutputRefError(nodeId, field, unparseableReason(nodeOutput.output));
    }
    const value = obj[field];
    // Required fields are guaranteed present (the producer validated post-parse),
    // so a missing/explicit-null value here is a declared-optional field → empty.

View on GitHub (pinned to 0773b97458)

Solutions

  1. Add `fieldName` to the producer's `output_format.properties` if the producer should emit it.
  2. Or correct the consumer's `$producer.field` reference to a declared field name.
  3. Or drop the `output_format` declaration (making the producer schemaless) only if untyped access is truly intended — this loses validation.

Example fix

// before
output_format:
  properties:
    summary: {type: string}
// after
output_format:
  properties:
    summary: {type: string}
    score: {type: number}
Defensive patterns

Strategy: validation

Validate before calling

function assertFieldDeclared(producer: { output_format?: Record<string, unknown> }, field: string): void {
  const props = producer.output_format?.properties as Record<string, unknown> | undefined;
  if (props && !(field in props)) {
    throw new Error(`$producer.${field} is not declared in output_format properties: ${Object.keys(props).join(', ')}`);
  }
}

Type guard

function isDeclaredField(schema: { properties?: Record<string, unknown> }, field: string): field is keyof schema['properties'] {
  return schema.properties !== undefined && field in schema.properties;
}

Try / catch

try {
  const { value } = resolveNodeOutputField(nodeOutput, nodeId, field);
} catch (err) {
  if (err instanceof OutputRefError && err.reason === 'not-in-schema') {
    console.error(`${field} missing from producer's declared output_format; fix schema or reference`);
  } else throw err;
}

Prevention

When it happens

Trigger: A consumer references `$producer.fieldName` where producer declares `output_format.properties` without `fieldName` (typo, renamed schema property, schema tightened after the consumer was written).

Common situations: 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.

Related errors


AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01). Data as JSON: /api/errors/55c1a352ec081e9c. Report an issue: GitHub.