toeverything/AFFiNE · error · StructuredResponseParseError
invalid_structured_output
invalid_structured_output
Error message
Structured response missing required output_json: ${response.output_text.trim().slice(0, 200)} What it means
parseNativeStructuredOutput() (native.ts:865) asserts that a native LLM structured response carries output_json. When output_json is undefined it throws StructuredResponseParseError, embedding the first 200 characters of output_text as evidence — meaning the model produced only free text and the structured JSON channel the caller requires is missing.
Source
Thrown at packages/backend/server/src/native.ts:875
): LlmRerankRequestContract {
return llmBuildRerankRequest({
model,
query: request.query,
candidates: request.candidates.map(candidate => ({
...(candidate.id ? { id: candidate.id } : {}),
text: candidate.text,
})),
...(request.topK ? { topN: request.topK } : {}),
});
}
export function parseNativeStructuredOutput(
response: Pick<LlmStructuredResponse, 'output_text'> & {
output_json?: unknown;
}
) {
if (response.output_json === undefined) {
throw new StructuredResponseParseError(
`Structured response missing required output_json: ${response.output_text
.trim()
.slice(0, 200)}`
);
}
return response.output_json;
}
export function llmValidateJsonSchema<T = unknown>(
schema: Record<string, unknown>,
value: T
): T {
if (!nativeLlmModule.llmValidateJsonSchema) {
throw new Error('native JSON schema validator is not available');
}
return nativeLlmModule.llmValidateJsonSchema(schema, value) as T;View on GitHub (pinned to b6de0ad51b)
Solutions
- Inspect the output_text snippet in the error — if JSON is embedded in prose, tighten the prompt and ensure the request enables the provider's structured/JSON mode so output_json gets populated.
- Verify the chosen provider and model support structured output and that the schema is attached to the request.
- Guard before parsing: only call parseNativeStructuredOutput when response.output_json !== undefined.
- Add a fallback that extracts/repairs JSON from output_text (strip code fences, run a JSON repair pass) when output_json is missing.
Example fix
// before
const json = parseNativeStructuredOutput(response); // throws when output_json is undefined
// after
if (response.output_json === undefined) {
throw new Error(
`LLM returned unstructured output: ${response.output_text.trim().slice(0, 200)}`
);
}
const json = parseNativeStructuredOutput(response); Defensive patterns
Strategy: type-guard
Validate before calling
if (response.output_json === undefined) {
// do not call parseNativeStructuredOutput; repair or fail explicitly
throw new Error(`Unstructured LLM output: ${response.output_text.slice(0, 200)}`);
} Type guard
function hasStructuredOutput(
r: Pick<LlmStructuredResponse, 'output_text'> & { output_json?: unknown }
): r is typeof r & { output_json: unknown } {
return r.output_json !== undefined;
} Try / catch
try {
const json = parseNativeStructuredOutput(response);
} catch (e) {
if (e instanceof StructuredResponseParseError) {
// inspect e.message snippet, retry with stricter prompt/schema
} else throw e;
} Prevention
- Always enable the provider's structured/JSON mode and attach the schema so output_json is populated.
- Guard output_json before parsing instead of parsing blindly.
- Keep a JSON-repair fallback for models that wrap JSON in prose.
When it happens
Trigger: Requesting structured output from the native LLM integration but receiving a response where output_json is undefined (only output_text populated), then passing that response into parseNativeStructuredOutput.
Common situations: Provider or model does not support JSON/structured mode; the request was built without the schema/response format so the integration never populated output_json; the model wrapped the JSON in prose or markdown fences; truncated generation; SDK version change altering the structured-output field.
Related errors
AI-assisted analysis of toeverything/AFFiNE@b6de0ad51b (2026-08-18).
Data as JSON: /api/errors/e28e2326c0ebf01c.
Report an issue: GitHub.