{"record":{"id":"b74aed948b79d4d2","repo":"can1357/oh-my-pi","slug":"this-task-requires-structured-output-matching-the","errorCode":null,"errorMessage":"This task requires structured output matching the declared schema; a last-turn result cannot satisfy it. Submit the full object: {\"result\":{\"data\":<object matching the schema>}}.","messagePattern":"This task requires structured output matching the declared schema; a last-turn result cannot satisfy it\\. Submit the full object: (.+?)\\}\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/yield.ts","lineNumber":431,"sourceCode":"\t\t// section's MAX_SCHEMA_RETRIES override flips schemaOverridden in finalization and the\n\t\t// stale section rides along untouched.\n\t\tif (status === \"success\" && isIncremental) {\n\t\t\tconst unknownLabels = this.#unknownIncrementalLabels(yieldType as string[]);\n\t\t\tif (unknownLabels.length > 0) {\n\t\t\t\tconst validLabels =\n\t\t\t\t\tthis.#knownSectionLabels.length > 0 ? formatYieldLabels(this.#knownSectionLabels) : \"none\";\n\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","sourceCodeStart":413,"sourceCodeEnd":449,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/yield.ts#L413-L449","documentation":"When a schema-bound task reaches its terminal last-turn yield with no accumulated incremental sections, the tool can only assemble raw prose — which finalization would reject post-mortem as a fatal schema_violation the subagent could no longer fix. To keep the failure correctable, execute() throws this retryable error up front, telling the caller to submit a full structured object instead.","triggerScenarios":"Calling yield with useLastTurn: true on a task that declares an output schema, while no incremental sections have been accumulated (#hasIncrementalSections is false) and status is success.","commonSituations":"A model finishes its work and tries to 'let the last message be the result' on a structured-output task; an agent template always sets useLastTurn regardless of whether the task is schema-bound.","solutions":["Instead of useLastTurn, submit the complete object: {\"result\":{\"data\":<object matching the schema>}}","Build the payload field-by-field against the declared JSON schema before yielding","If prose really is intended, switch the task to an unstructured (no-schema) output configuration"],"exampleFix":"// before\nyield({ result: { useLastTurn: true } });\n// after\nyield({ result: { data: { summary: \"...\", findings: [] } } });","handlingStrategy":"validation","validationCode":"if (useLastTurn && taskHasOutputSchema && !hasAccumulatedSections()) {\n  throw new TypeError('useLastTurn is invalid for schema-bound tasks without sections; submit full data object');\n}","typeGuard":"function canUseLastTurn(result, schemaBound, hasSections) {\n  return !(schemaBound && result.useLastTurn === true && !hasSections);\n}","tryCatchPattern":"try {\n  yield({ result: { useLastTurn: true } });\n} catch (err) {\n  if (err.message.includes('structured output')) {\n    yield({ result: { data: assembleSchemaObject() } });\n  } else throw err;\n}","preventionTips":["Only set useLastTurn on unstructured (no-schema) tasks","For schema-bound tasks, always build and submit the full data object","Ensure at least one incremental section exists before relying on last-turn assembly"],"tags":["tool-call","yield","schema","structured-output","subagent"],"backgroundTag":"structured-output-required","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}