{"record":{"id":"94c8eb65e9eb870c","repo":"can1357/oh-my-pi","slug":"completion-did-not-return-a-structured-response","errorCode":null,"errorMessage":"completion() did not return a structured response matching the schema.","messagePattern":"completion\\(\\) did not return a structured response matching the schema\\.","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/eval/completion-bridge.ts","lineNumber":192,"sourceCode":"\t\tthrow new ToolError(response.errorMessage ?? \"completion() request failed.\");\n\t}\n\tif (response.stopReason === \"aborted\") {\n\t\tthrow new ToolError(\"completion() request aborted.\");\n\t}\n\n\tlet resultText: string;\n\tif (schema) {\n\t\tconst call = extractToolCall(response, STRUCTURED_TOOL_NAME);\n\t\tlet value: unknown;\n\t\tif (call) {\n\t\t\tvalue = call.arguments;\n\t\t} else {\n\t\t\tconst text = extractTextContent(response);\n\t\t\tif (!text) throw new ToolError(\"completion() returned no structured response.\");\n\t\t\ttry {\n\t\t\t\tvalue = parseJsonPayload(text);\n\t\t\t} catch {\n\t\t\t\tthrow new ToolError(\"completion() did not return a structured response matching the schema.\");\n\t\t\t}\n\t\t}\n\t\tresultText = JSON.stringify(value);\n\t} else {\n\t\tresultText = extractTextContent(response);\n\t\tif (!resultText) throw new ToolError(\"completion() returned no text output.\");\n\t}\n\n\toptions.emitStatus?.({\n\t\top: \"completion\",\n\t\tmodel: formatModelString(model),\n\t\ttier: finalTier,\n\t\tchars: resultText.length,\n\t});\n\n\treturn {\n\t\ttext: resultText,\n\t\tdetails: { model: formatModelString(model), tier: finalTier, structured: Boolean(schema) },","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/eval/completion-bridge.ts#L174-L210","documentation":"With a `schema` supplied, the bridge expects either a forced `respond` tool call or JSON text it can parse with `parseJsonPayload`. When the model returns non-empty plain text that is not parseable JSON, this ToolError is thrown. It means the model answered but not in the structured shape requested.","triggerScenarios":"`completion(prompt, { schema })` where the model declines the tool call and replies with prose like \"Here is the data: ...\" or a fenced code block with trailing commentary that `parseJsonPayload` cannot extract.","commonSituations":"Weaker models ignoring toolChoice on providers that don't enforce it; models wrapping JSON in markdown fences with explanation text; prompts that ask a question conversationally so the model answers in prose; schemas too complex for a smol model.","solutions":["Retry, or move to a stronger tier (`model: \"slow\"`) that reliably makes the forced tool call.","Reword the prompt to demand strict JSON with no prose (\"Respond only with JSON matching ...\").","Simplify the schema — fewer/nested-fewer fields improves tool-call compliance.","In the caller, catch this error and fall back to a second attempt or a heuristic parse of the raw text."],"exampleFix":"// before\nconst v = JSON.parse((await completion(q, { schema })).text); // may throw\n// after: tolerate prose replies\ntry {\n  var val = JSON.parse((await completion(q, { schema })).text);\n} catch {\n  var val = await completion(q + \" Respond ONLY with JSON.\", { model: \"slow\", schema });\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const r = await completion(prompt, { schema });\n  return JSON.parse(r.text);\n} catch (e) {\n  if (String(e).includes(\"structured response\")) {\n    const r2 = await completion(prompt + \"\\nRespond ONLY with JSON.\", { model: \"slow\", schema });\n    return JSON.parse(r2.text);\n  }\n  throw e;\n}","preventionTips":["Prefer tiers with strong tool-call compliance for schema work.","Phrase prompts to demand raw JSON with no commentary.","Validate/sanitize model text output before parsing (strip fences).","Retry with an upgraded tier on structured-output failures."],"tags":["structured-output","llm-output","json-parsing"],"backgroundTag":"schema-mismatch","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}