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

  1. 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.
  2. Verify the chosen provider and model support structured output and that the schema is attached to the request.
  3. Guard before parsing: only call parseNativeStructuredOutput when response.output_json !== undefined.
  4. 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

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.