can1357/oh-my-pi · error · ToolError

completion() returned no text output.

Error message

completion() returned no text output.

What it means

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).

Source

Thrown at packages/coding-agent/src/eval/completion-bridge.ts:198

	let resultText: string;
	if (schema) {
		const call = extractToolCall(response, STRUCTURED_TOOL_NAME);
		let value: unknown;
		if (call) {
			value = call.arguments;
		} else {
			const text = extractTextContent(response);
			if (!text) throw new ToolError("completion() returned no structured response.");
			try {
				value = parseJsonPayload(text);
			} catch {
				throw new ToolError("completion() did not return a structured response matching the schema.");
			}
		}
		resultText = JSON.stringify(value);
	} else {
		resultText = extractTextContent(response);
		if (!resultText) throw new ToolError("completion() returned no text output.");
	}

	options.emitStatus?.({
		op: "completion",
		model: formatModelString(model),
		tier: finalTier,
		chars: resultText.length,
	});

	return {
		text: resultText,
		details: { model: formatModelString(model), tier: finalTier, structured: Boolean(schema) },
	};
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Retry — degenerate finishes are usually transient.
  2. Pass a system prompt that instructs the model to answer in plain text (the `system` option).
  3. Use a stronger/different tier or model that reliably emits text.
  4. Shorten or reword the prompt; very short prompts can trigger empty finishes on small models.

Example fix

// before
const t = (await completion("hi")).text; // can throw on empty output
// after
const t = (await completion("hi", { system: "Always answer in plain text." })).text;
Defensive patterns

Strategy: retry

Try / catch

let r;
try {
  r = await completion(prompt);
} catch (e) {
  if (String(e).includes("no text output")) r = await completion(prompt, { system: "Always answer in plain text." });
  else throw e;
}

Prevention

When it happens

Trigger: `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.

Common situations: 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.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/04880710168e7ad1. Report an issue: GitHub.