can1357/oh-my-pi · error
This task requires structured output matching the declared s
Error message
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>}}. What it means
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.
Source
Thrown at packages/coding-agent/src/tools/yield.ts:431
// section's MAX_SCHEMA_RETRIES override flips schemaOverridden in finalization and the
// stale section rides along untouched.
if (status === "success" && isIncremental) {
const unknownLabels = this.#unknownIncrementalLabels(yieldType as string[]);
if (unknownLabels.length > 0) {
const validLabels =
this.#knownSectionLabels.length > 0 ? formatYieldLabels(this.#knownSectionLabels) : "none";
throw new Error(
`Section ${formatYieldLabels(yieldType as string[])} uses unknown incremental yield label(s): ${formatYieldLabels(unknownLabels)}. Resubmit with one of the schema's labels: ${validLabels}.`,
);
}
}
// A schema-bound terminal last-turn yield with no accumulated sections can
// only assemble raw prose, which finalization then rejects post-mortem as a
// fatal schema_violation the child can no longer correct. Catch it here as
// a retryable error instead. With sections present, a data-less finalize
// legitimately closes the incremental flow (assembly keeps the sections).
if (status === "success" && useLastTurn && !isIncremental && this.#validate && !this.#hasIncrementalSections) {
throw new Error(
"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>}}.`,
);
}
if (status === "success" && !useLastTurn) {
if (data === null) {
throw new Error("data is required when yield indicates success");
}
const validateData = (value: unknown): JsonSchemaValidationResult | undefined =>
isIncremental
? this.#validateIncrementalSection(yieldType as string[], value)
: this.#validate
? this.#validate(value)
: undefined;
let sectionFailure = validateData(data);
if (sectionFailure && !sectionFailure.success && typeof data === "string") {
// Lossless recovery: a JSON-encoded payload string parses to exactly
// the intended value (executor finalization already parses terminalView on GitHub (pinned to 9690622007)
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
Example fix
// before
yield({ result: { useLastTurn: true } });
// after
yield({ result: { data: { summary: "...", findings: [] } } }); Defensive patterns
Strategy: validation
Validate before calling
if (useLastTurn && taskHasOutputSchema && !hasAccumulatedSections()) {
throw new TypeError('useLastTurn is invalid for schema-bound tasks without sections; submit full data object');
} Type guard
function canUseLastTurn(result, schemaBound, hasSections) {
return !(schemaBound && result.useLastTurn === true && !hasSections);
} Try / catch
try {
yield({ result: { useLastTurn: true } });
} catch (err) {
if (err.message.includes('structured output')) {
yield({ result: { data: assembleSchemaObject() } });
} else throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Section ${formatYieldLabels(yieldType as string[])} uses unk
- result cannot contain both data and error
- result must contain either \`data\` or \`error\`. Use \`{res
- data is required when yield indicates success
- ${scope} does not match schema: ${formatAllValidationIssues(
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/b74aed948b79d4d2.
Report an issue: GitHub.