{"record":{"id":"045991b66a1ee86a","repo":"can1357/oh-my-pi","slug":"data-is-required-when-yield-indicates-success","errorCode":null,"errorMessage":"data is required when yield indicates success","messagePattern":"data is required when yield indicates success","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/yield.ts","lineNumber":438,"sourceCode":"\t\t\t\tthrow new Error(\n\t\t\t\t\t`Section ${formatYieldLabels(yieldType as string[])} uses unknown incremental yield label(s): ${formatYieldLabels(unknownLabels)}. Resubmit with one of the schema's labels: ${validLabels}.`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\t// A schema-bound terminal last-turn yield with no accumulated sections can\n\t\t// only assemble raw prose, which finalization then rejects post-mortem as a\n\t\t// fatal schema_violation the child can no longer correct. Catch it here as\n\t\t// a retryable error instead. With sections present, a data-less finalize\n\t\t// legitimately closes the incremental flow (assembly keeps the sections).\n\t\tif (status === \"success\" && useLastTurn && !isIncremental && this.#validate && !this.#hasIncrementalSections) {\n\t\t\tthrow new Error(\n\t\t\t\t\"This task requires structured output matching the declared schema; a last-turn result cannot satisfy it. \" +\n\t\t\t\t\t`Submit the full object: {\"result\":{\"data\":<object matching the schema>}}.`,\n\t\t\t);\n\t\t}\n\t\tif (status === \"success\" && !useLastTurn) {\n\t\t\tif (data === null) {\n\t\t\t\tthrow new Error(\"data is required when yield indicates success\");\n\t\t\t}\n\t\t\tconst validateData = (value: unknown): JsonSchemaValidationResult | undefined =>\n\t\t\t\tisIncremental\n\t\t\t\t\t? this.#validateIncrementalSection(yieldType as string[], value)\n\t\t\t\t\t: this.#validate\n\t\t\t\t\t\t? this.#validate(value)\n\t\t\t\t\t\t: undefined;\n\t\t\tlet sectionFailure = validateData(data);\n\t\t\tif (sectionFailure && !sectionFailure.success && typeof data === \"string\") {\n\t\t\t\t// Lossless recovery: a JSON-encoded payload string parses to exactly\n\t\t\t\t// the intended value (executor finalization already parses terminal\n\t\t\t\t// yields the same way). Never the reverse — stringifying objects to\n\t\t\t\t// fit string-typed fields is silent corruption.\n\t\t\t\tconst parsed = parseJsonContainerString(data);\n\t\t\t\tif (parsed !== undefined) {\n\t\t\t\t\tconst revalidated = validateData(parsed);\n\t\t\t\t\tif (revalidated === undefined || revalidated.success) {\n\t\t\t\t\t\tdata = parsed;","sourceCodeStart":420,"sourceCodeEnd":456,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/yield.ts#L420-L456","documentation":"A yield that signals success without useLastTurn must include a `data` payload. If data is null (or absent) on a non-last-turn success, the tool throws because a successful result with no content cannot be assembled or validated. Error-status yields and useLastTurn yields bypass this check.","triggerScenarios":"Calling yield with a success status, useLastTurn not set, and data explicitly null or omitted — e.g. {result: {data: null}} or {result: {status: \"success\"}}.","commonSituations":"A caller converts a falsy result (empty string, 0, false) into null before yielding; a template leaves the data field blank; an agent finishes with 'nothing to report' instead of yielding an empty-but-schema-valid object.","solutions":["Resubmit with {result: {data: <your output>}}, matching the declared schema even if the content is empty (e.g. {items: []})","If there is genuinely no result and the task failed, use {result: {error: \"reason\"}} instead","If the answer lives in the final assistant message, use the useLastTurn variant rather than null data","Fix the caller so falsy-but-valid values are not coerced to null"],"exampleFix":"// before\nyield({ result: { data: null } });\n// after\nyield({ result: { data: { items: [], summary: \"no findings\" } } });","handlingStrategy":"validation","validationCode":"if (status === 'success' && !useLastTurn && (data === null || data === undefined)) {\n  throw new TypeError('success yield requires a data payload');\n}","typeGuard":"function successHasData(r) {\n  return !(r.status === 'success' && r.useLastTurn !== true && (r.data === null || r.data === undefined));\n}","tryCatchPattern":"try {\n  yield({ result: { data } });\n} catch (err) {\n  if (err.message === 'data is required when yield indicates success') {\n    yield({ result: { data: emptyButValidObject() } });\n  } else throw err;\n}","preventionTips":["Never coerce results to null — preserve falsy-but-valid values (0, \"\", false)","For 'nothing found', yield a schema-valid empty object (e.g. {items: []})","Route genuine failures through {result:{error}} instead of null data"],"tags":["tool-call","yield","api-misuse","null-value","subagent"],"backgroundTag":"missing-required-field","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}