{"record":{"id":"5deb5091fb5cfd38","repo":"coleam00/Archon","slug":"workflow-outcome-field-field-on-returns-node","errorCode":null,"errorMessage":"Workflow outcome_field '${field}' on returns node '${returns}' did not resolve to a boolean","messagePattern":"Workflow outcome_field '(.+?)' on returns node '(.+?)' did not resolve to a boolean","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/workflows/src/dag-executor.ts","lineNumber":11623,"sourceCode":"\n  // Persist the authored verdict as soon as the selected result is available,\n  // independently from every lifecycle branch below (#2618). The initial call\n  // covers a selected node rehydrated from node_completed events on resume; the\n  // awaited per-layer hook captures a fresh result before later work can pause or\n  // fail; and the unwind backstop covers a fatal throw while aggregating that layer.\n  // A same-value write is skipped, but a genuine re-execution may replace the\n  // prior verdict.\n  let persistedOutcome: WorkflowRunOutcome | null = workflowRun.outcome;\n  const persistAuthoredOutcome = async (): Promise<void> => {\n    const field = workflow.outcome_field;\n    const returns = workflow.returns;\n    if (field === undefined || returns === undefined) return;\n    const selectedOutput = nodeOutputs.get(returns);\n    if (selectedOutput?.state !== 'completed') return;\n\n    const resolution = resolveNodeOutputField(selectedOutput, returns, field);\n    if (resolution.kind !== 'value' || typeof resolution.value !== 'boolean') {\n      throw new Error(\n        `Workflow outcome_field '${field}' on returns node '${returns}' did not resolve to a boolean`\n      );\n    }\n    const outcome: WorkflowRunOutcome = resolution.value ? 'succeeded' : 'failed';\n    if (outcome === persistedOutcome) return;\n    await deps.store.updateWorkflowRun(workflowRun.id, { outcome });\n    persistedOutcome = outcome;\n  };\n\n  // Run the topological layers. runLayers mutates the context's mutable fields in place\n  // (nodeOutputs, lastSequentialSession, usage accumulators); we read them back below\n  // for the terminal tally. stepNamePrefix is '' for the top-level DAG so node event\n  // step_names are the raw node ids (identical to pre-refactor behavior). Fields stay\n  // explicit because spreading options would also copy executor-only state into this\n  // context without an excess-property check.\n  const runCtx: RunLayersContext = {\n    deps,\n    platform,","sourceCodeStart":11605,"sourceCodeEnd":11641,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/workflows/src/dag-executor.ts#L11605-L11641","documentation":"Workflows may declare an outcome_field: the executor reads a boolean output field from the designated returns node after completion to classify the run as succeeded or failed. If the field cannot be resolved to an actual boolean value on that node's completed output, the executor throws rather than guessing the outcome.","triggerScenarios":"The returns node completed but its output_format produces a non-boolean for outcome_field: field missing, nested under a different path, typed string/number, or $async resolution returned something other than a concrete boolean value.","commonSituations":"Typos in the field name versus output_format schema; schema says boolean but a prompt returned a string like \"true\"; output_format changed after the outcome_field was written; returns node id renamed so nodeOutputs holds a different node's data.","solutions":["Make the output_format declare outcome_field as type: boolean and verify the node's actual output matches","Fix the field path/name to match the returns node's output structure exactly","Add a coercion or normalization step: have the returns node emit a strict boolean (e.g. via output_format constraints) rather than a string","Update outcome_field to point at the correct returns node id if the graph was refactored"],"exampleFix":"// before\nreturns: publish\noutcome_field: was_successful   # not in schema / string\noutput_format:\n  type: object\n  properties:\n    was_successful: { type: string }\n// after\noutput_format:\n  type: object\n  properties:\n    was_successful: { type: boolean }","handlingStrategy":"type-guard","validationCode":"const out = nodeOutputs.get(returnsNodeId);\nif (out?.state === 'completed') {\n  const v = resolveNodeOutputField(out, returnsNodeId, outcomeField);\n  if (v.kind !== 'value' || typeof v.value !== 'boolean') throw new Error('outcome_field must resolve to a boolean');\n}","typeGuard":"function isBooleanResolution(r: Resolution): r is { kind: 'value'; value: boolean } {\n  return r.kind === 'value' && typeof r.value === 'boolean';\n}","tryCatchPattern":"try {\n  await engine.run(workflow);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('did not resolve to a boolean')) {\n    // fix outcome_field path or coerce the node output to a strict boolean\n  } else throw err;\n}","preventionTips":["Declare outcome_field as type: boolean in the returns node's output_format","Keep outcome_field in sync with the output schema; update both together","Prompt for strict booleans (avoid \"true\" strings) when the field classifies the run"],"tags":["workflow-config","schema-validation","outcome-field"],"backgroundTag":"schema-validation-failed","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}