{"record":{"id":"04880710168e7ad1","repo":"can1357/oh-my-pi","slug":"completion-returned-no-text-output","errorCode":null,"errorMessage":"completion() returned no text output.","messagePattern":"completion\\(\\) returned no text output\\.","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/eval/completion-bridge.ts","lineNumber":198,"sourceCode":"\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) },\n\t};\n}\n","sourceCodeStart":180,"sourceCodeEnd":213,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/eval/completion-bridge.ts#L180-L213","documentation":"In the non-structured path (no `schema`), the bridge returns `extractTextContent(response)` as the result. This ToolError is thrown when that yields nothing — the model finished (successfully, not aborted or errored) but produced no text content blocks (e.g. only tool calls or empty content).","triggerScenarios":"`completion(prompt)` where the response's stop reason is normal (stop/end_turn) but the message contains no text blocks — content is empty, contains only tool_use/thinking blocks, or text blocks are empty strings.","commonSituations":"Model ends immediately with a tool call despite no tools being offered (rare provider quirk); response consisting solely of reasoning blocks on a thinking model; safety filter stripping the content; degenerate finish on a tiny model.","solutions":["Retry — degenerate finishes are usually transient.","Pass a system prompt that instructs the model to answer in plain text (the `system` option).","Use a stronger/different tier or model that reliably emits text.","Shorten or reword the prompt; very short prompts can trigger empty finishes on small models."],"exampleFix":"// before\nconst t = (await completion(\"hi\")).text; // can throw on empty output\n// after\nconst t = (await completion(\"hi\", { system: \"Always answer in plain text.\" })).text;","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"let r;\ntry {\n  r = await completion(prompt);\n} catch (e) {\n  if (String(e).includes(\"no text output\")) r = await completion(prompt, { system: \"Always answer in plain text.\" });\n  else throw e;\n}","preventionTips":["Always pass a system prompt instructing plain-text answers.","Avoid tiny models for one-shot text completions.","Retry once on empty-output errors before failing the cell."],"tags":["llm-output","empty-response"],"backgroundTag":"empty-model-response","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}