{"record":{"id":"894a22eb9e9c7116","repo":"coleam00/Archon","slug":"cannot-generate-dry-run-stub-for-node-node-id-894a22","errorCode":null,"errorMessage":"Cannot generate dry-run stub for node '${node.id}': loop.until_field requires an object-typed output_format","messagePattern":"Cannot generate dry-run stub for node '(.+?)': loop\\.until_field requires an object-typed output_format","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/workflows/src/dry-run.ts","lineNumber":173,"sourceCode":"    default:\n      return 'TODO';\n  }\n}\n\nfunction generatedStubFor(node: DagNode): DryRunStubValue {\n  if (node.output_format === undefined) {\n    return isLoopNode(node) && node.loop.until !== undefined ? node.loop.until : 'TODO';\n  }\n  if (node.output_format.$async === true) {\n    throw new Error(\n      `Cannot generate dry-run stub for node '${node.id}': asynchronous output_format schemas are unsupported`\n    );\n  }\n\n  const value = schemaPlaceholder(node.output_format);\n  if (isLoopNode(node) && node.loop.until_field !== undefined) {\n    if (!isRecord(value)) {\n      throw new Error(\n        `Cannot generate dry-run stub for node '${node.id}': loop.until_field requires an object-typed output_format`\n      );\n    }\n    value[node.loop.until_field] = true;\n  }\n\n  let compileError: string | undefined;\n  const validation = validateStructuredOutput(value, node.output_format, message => {\n    compileError = message;\n  });\n  if (compileError !== undefined) {\n    throw new Error(\n      `Cannot generate dry-run stub for node '${node.id}': output_format could not be compiled (${compileError})`\n    );\n  }\n  if (!validation.valid) {\n    throw new Error(\n      `Cannot generate schema-valid dry-run stub for node '${node.id}': ${validation.errors.join('; ')}`","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/workflows/src/dry-run.ts#L155-L191","documentation":"When a loop node uses loop.until_field, the dry-run stub generator sets that field to true in a synthesized object stub so the loop terminates in dry-run. This requires the output_format placeholder to be an object; if schemaPlaceholder produced a non-record value (scalar, array, etc.), the generator throws instead of mutating a non-object.","triggerScenarios":"A loop node combines loop.until_field with an output_format whose placeholder is not an object — e.g. output_format type: string/number/array, or a schema whose generated placeholder fails isRecord — during dry-run stub generation.","commonSituations":"Writing loop.until_field against a loop node whose output_format declares a primitive or array type; forgetting that until_field addressing requires the node output to be an object; a schema typo (missing type: object) so the placeholder degrades to a scalar.","solutions":["Declare the loop node's output_format as type: object with the until_field as a boolean property","Move loop.until_field onto a node whose output is genuinely an object","If the loop checks a non-object output, switch the loop to a different until mechanism (e.g. loop.until expression evaluated on the value) that matches the output type","Validate the workflow with the schema linter before dry-run to catch the type mismatch early"],"exampleFix":"// before\nloop:\n  until_field: done\noutput_format:\n  type: string\n// after\nloop:\n  until_field: done\noutput_format:\n  type: object\n  properties:\n    done: { type: boolean }\n    result: { type: string }","handlingStrategy":"validation","validationCode":"if (isLoopNode(node) && node.loop.until_field !== undefined) {\n  const stub = schemaPlaceholder(node.output_format);\n  if (!isRecord(stub)) throw new Error(`node ${node.id}: until_field requires object output_format`);\n}","typeGuard":"function isObjectOutputForUntilField(node: DagNode): boolean {\n  if (!isLoopNode(node) || node.loop.until_field === undefined) return true;\n  return node.output_format?.type === 'object';\n}","tryCatchPattern":"try {\n  await engine.dryRun(workflow);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('requires an object-typed output_format')) {\n    // change output_format to type: object with the until_field boolean property\n  } else throw err;\n}","preventionTips":["Whenever loop.until_field is used, declare output_format as an object containing that boolean field","Validate loop node schemas (type: object) before dry-run","Prefer boolean until fields in the schema so the generated stub can terminate the loop"],"tags":["dry-run","loop-node","output-format"],"backgroundTag":"schema-validation-failed","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}