{"record":{"id":"a9768cd3cabf7829","repo":"can1357/oh-my-pi","slug":"unexpected-stop-online-classification-failed-r","errorCode":null,"errorMessage":"unexpected-stop: online classification failed: ${response.errorMessage ?? \"unknown error\"}","messagePattern":"unexpected-stop: online classification failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/session/unexpected-stop-classifier.ts","lineNumber":120,"sourceCode":"\t\t\tcompleteSimple(\n\t\t\t\tmodel,\n\t\t\t\t{\n\t\t\t\t\tsystemPrompt: [CLASSIFIER_SYSTEM_PROMPT],\n\t\t\t\t\tmessages: [{ role: \"user\", content: text, timestamp: Date.now() }],\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tapiKey: deps.registry.resolver(model, deps.sessionId),\n\t\t\t\t\tmaxTokens,\n\t\t\t\t\tdisableReasoning: true,\n\t\t\t\t\tmetadata,\n\t\t\t\t\tsignal: deps.signal,\n\t\t\t\t},\n\t\t\t),\n\t\t{ signal: deps.signal },\n\t);\n\n\tif (response.stopReason === \"error\") {\n\t\tthrow new Error(`unexpected-stop: online classification failed: ${response.errorMessage ?? \"unknown error\"}`);\n\t}\n\n\tconst outputText = response.content\n\t\t.filter((part): part is { type: \"text\"; text: string } => part.type === \"text\")\n\t\t.map(part => part.text)\n\t\t.join(\"\\n\");\n\treturn parseUnexpectedStopClassification(outputText);\n}\n\nasync function classifyLocal(\n\ttext: string,\n\tmodelKey: string,\n\tdeps: ClassifyUnexpectedStopDeps,\n): Promise<boolean | undefined> {\n\tif (!isTinyMemoryLocalModelKey(modelKey)) {\n\t\tthrow new Error(`unexpected-stop: unsupported local classifier model: ${modelKey}`);\n\t}\n\tconst builtPrompt = prompt.render(unexpectedStopClassifierPrompt, { message: text });","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/session/unexpected-stop-classifier.ts#L102-L138","documentation":"The online classifier's completion call returned stopReason === 'error'; classifyOnline surfaces the provider's errorMessage wrapped in this prefix. It is thrown after retryTransientCompletion exhausted retries, so the underlying failure is persistent (auth, rate limit, network, or model rejection).","triggerScenarios":"The streaming completion request to the tiny/smol model fails with an error stop reason on every retry: 401/403 from bad key, 429 rate limits, network outage, or the model rejects the request shape (e.g. maxTokens too small for a reasoning model).","commonSituations":"Expired/invalid API key; provider outage; rate limiting from bursty classification calls; using a reasoning model with ONLINE_REASONING_SAFE_MAX_TOKENS too low causing truncation errors; proxy/firewall blocking the endpoint.","solutions":["Read the embedded response.errorMessage to identify the root cause (auth vs rate limit vs network).","Fix the underlying credential or network issue and retry.","Increase maxTokens if the error indicates the reasoning model exceeded the token budget.","Verify the tiny/smol model is still served by the provider (deprecated ids return errors)."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  verdict = await classifyUnexpectedStop(text, deps);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"unexpected-stop: online classification failed:\")) {\n    logger.warn(\"stop classification failed\", { detail: err.message });\n    verdict = undefined; // or retry with backoff for transient causes\n  } else throw err;\n}","preventionTips":["Inspect the embedded errorMessage to distinguish auth (fix keys), rate limit (backoff), and network (retry) causes.","Keep ONLINE_REASONING_SAFE_MAX_TOKENS large enough for reasoning-style small models.","Add jittered backoff around classification calls to avoid 429s.","Pin the tiny/smol model to a stable, non-deprecated id."],"tags":["llm-api","classification","request-failed"],"backgroundTag":"llm-request-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}