coleam00/Archon · error · Error

buildMessage(nodeOutput) (dynamic caller-supplied message…

Error message

buildMessage(nodeOutput) (dynamic caller-supplied message about a failed producer)

What it means

assertProducerNotFailed throws a plain Error when a NodeOutput being consumed has state 'failed'. It centralizes the 'a producer node failed, so its output cannot be read' invariant for output-ref resolution; callers supply buildMessage so the thrown message describes the specific failed producer and the consuming site.

Solutions

  1. Inspect the referenced producer node's failure (its error, logs, exit status) and fix the upstream step so it succeeds
  2. Add a retry policy to the producer node if the failure is transient
  3. Gate the consuming node with a condition/when so it does not run after a producer failure, or supply a fallback default for the ref
  4. If the failure is expected, make the producer emit a structured failed-but-handled state instead of relying on the ref to fail

Example fix

// before: downstream always reads upstream output
const out = resolveOutputRef(refs.steps.build, binding);
// after: guard the consumer on producer state
const out = refs.steps.build.state === 'failed'
  ? fallbackValue
  : resolveOutputRef(refs.steps.build, binding);
Defensive patterns

Strategy: try-catch

Validate before calling

// check producer state before resolving
if (nodeOutput.state === 'failed') {
  throw new Error(`producer failed: ${nodeOutput.error ?? 'unknown'}`);
}

Type guard

function producerFailed(out: NodeOutput): out is Extract<NodeOutput, { state: 'failed' }> {
  return out.state === 'failed';
}

Try / catch

try {
  const value = resolveOutputRef(nodeOutput, binding);
} catch (err) {
  log.warn({ err }, 'producer_output_unavailable');
  const value = fallbackValue; // explicit, documented fallback
}

Prevention

When it happens

Trigger: Resolving a workflow output reference (resolveOutputRef), computing the whole-ref logical value (wholeRefLogicalValue), resolving a binding directive, or substituting ${node.output} style references, when the referenced node's NodeOutput.state === 'failed'.

Common situations: A downstream step references an upstream step's output, but the upstream step crashed, exited non-zero, or its agent run failed; the workflow author then sees a resolution error instead of undefined data.

Related errors


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

Appendix: source

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

 * 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.
 */
export function assertProducerNotFailed(
  nodeOutput: NodeOutput,
  buildMessage: (failed: Extract<NodeOutput, { state: 'failed' }>) => string
): void {
  if (nodeOutput.state === 'failed') {
    throw new Error(buildMessage(nodeOutput));
  }
}

View on GitHub (pinned to 0773b97458)