can1357/oh-my-pi · error
auto-thinking: unparseable local classification: ${JSON.stri
Error message
auto-thinking: unparseable local classification: ${JSON.stringify(text)} What it means
The local auto-thinking classifier asks a tiny local model to label a prompt with a difficulty bucket (e.g. low/medium/high/max). classifyLocal parses the model's output with parseDifficultyBucket; if the returned text cannot be mapped to an Effort bucket, it throws this error naming the offending text.
Source
Thrown at packages/coding-agent/src/auto-thinking/classifier.ts:176
async function classifyLocal(input: string, modelKey: string, deps: ClassifyDifficultyDeps): Promise<Effort> {
if (!isTinyMemoryLocalModelKey(modelKey)) {
throw new Error(`auto-thinking: unsupported local classifier model: ${modelKey}`);
}
const maxTokens = isTinyMemoryReasoningModelKey(modelKey)
? Math.max(LOCAL_ANSWER_MAX_TOKENS, LOCAL_REASONING_MAX_TOKENS)
: LOCAL_ANSWER_MAX_TOKENS;
const builtPrompt = prompt.render(difficultyLocalPrompt, { prompt: input });
const text = await tinyModelClient.complete(modelKey, builtPrompt, {
maxTokens,
signal: deps.signal,
});
if (!text) {
throw new Error("auto-thinking: local classification returned no output");
}
const effort = parseDifficultyBucket(text);
if (!effort) {
throw new Error(`auto-thinking: unparseable local classification: ${JSON.stringify(text)}`);
}
return effort;
}
/**
* Map an online level keyword to an {@link Effort}; earliest match wins.
*
* `max` is only offered to the classifier when the target model exposes that
* tier, but it is always parsed: an unsupported `max` is snapped back down by
* {@link clampAutoThinkingEffort} rather than failing the turn.
*/
export function parseDifficultyLevel(text: string): Effort | undefined {
const lower = text.toLowerCase();
const candidates: Array<[number, Effort]> = [];
// `xhigh` must be probed as its own token: `\bhigh\b` cannot match the "high"
// inside "xhigh" (no word boundary between `x` and `h`), so the two never collide.
const xhigh = lower.search(/x[\s_-]?high/);
if (xhigh >= 0) candidates.push([xhigh, Effort.XHigh]);View on GitHub (pinned to 9690622007)
Solutions
- Inspect the JSON-embedded text in the message to see what the model actually returned
- Retry the classification — local tiny models can produce flaky output
- Verify the configured classifier model is the intended tiny local model and that reasoning output is not polluting the answer
- If it reproduces consistently, check parseDifficultyBucket's accepted keywords against the local prompt's answer format
Defensive patterns
Strategy: retry
Validate before calling
const text = await tinyModelClient.complete(modelKey, builtPrompt, { maxTokens, signal });
if (!text || !parseDifficultyBucket(text)) {
// fall back / retry before surfacing the error
} Try / catch
try {
const effort = await effort(input);
} catch (err) {
if (String((err as Error).message).includes("unparseable local classification")) {
effort = DEFAULT_EFFORT; // or retry once
} else throw err;
} Prevention
- Retry classification once on unparseable output before failing the turn
- Keep maxTokens high enough that reasoning models finish within the budget
- Use the prompt format the local model was validated against
- Fall back to a default effort bucket instead of failing the whole task
When it happens
Trigger: classifyLocal is called (via effort) with a local tiny-model key, tinyModelClient.complete returns non-empty text, but parseDifficultyBucket finds no valid bucket keyword in it — e.g. the model answers with reasoning, a full sentence, or a different label format.
Common situations: Local model outputs verbose prose instead of the expected single keyword; a reasoning-enabled tiny model burns tokens on thinking and answers oddly; the prompt was changed or the model is misconfigured so its reply format drifts.
Related errors
- unexpected-stop: unsupported local classifier model: ${model
- Replacement text is not valid UTF-8: {err}
- V2 compaction stream parse failed: ${err instanceof Error ?
- ${source} response is missing credentials.
- ambiguous family for `${model}`: `${tied[0]}` and `${tied[1]
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/3e2c05b1cef04f81.
Report an issue: GitHub.