{"record":{"id":"73f0ec5df5c92ce8","repo":"HKUDS/DeepTutor","slug":"local-llm-error-error-text","errorCode":null,"errorMessage":"Local LLM error: {error_text}","messagePattern":"Local LLM error: (.+?)","errorType":"http","errorClass":"LLMAPIError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/llm/local_provider.py","lineNumber":219,"sourceCode":"    }\n\n    # Add optional parameters\n    if kwargs.get(\"max_tokens\"):\n        data[\"max_tokens\"] = kwargs[\"max_tokens\"]\n    if isinstance(kwargs.get(\"response_format\"), dict):\n        data[\"response_format\"] = kwargs[\"response_format\"]\n\n    timeout_value = kwargs.get(\"timeout\", DEFAULT_TIMEOUT)\n    timeout_seconds = (\n        float(timeout_value) if isinstance(timeout_value, (int, float)) else DEFAULT_TIMEOUT\n    )\n    timeout = aiohttp.ClientTimeout(total=timeout_seconds)\n\n    async with aiohttp.ClientSession(timeout=timeout) as session:\n        async with session.post(url, json=data, headers=headers) as response:\n            if response.status != 200:\n                error_text = await response.text()\n                raise LLMAPIError(\n                    f\"Local LLM error: {error_text}\",\n                    status_code=response.status,\n                    provider=\"local\",\n                )\n\n            result = await response.json()\n            content = _extract_message_from_payload(result)\n            content = clean_thinking_tags(content)\n            if content:\n                return content\n\n            logger.warning(\"Local LLM returned no choices: %s\", result)\n            return \"\"\n\n\nasync def stream(\n    prompt: str,\n    system_prompt: str = \"You are a helpful assistant.\",","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/llm/local_provider.py#L201-L237","documentation":"The local LLM server returned a non-200 HTTP status and the response body is surfaced verbatim in an LLMAPIError with status_code and provider='local'. This is the local backend rejecting the request (bad model name, malformed payload, server overload, etc.).","triggerScenarios":"complete() posts to {base_url}/chat/completions and the server returns 404 (wrong model or path), 400 (malformed messages), 500 (model crashed/OOM), or 503 (model loading).","commonSituations":"Requesting a model that isn't pulled/loaded in Ollama; pointing base_url at a server without an OpenAI-compatible /chat/completions route; local server out of memory.","solutions":["Read the error_text embedded in the message — it names the exact server-side cause.","If 404/model-not-found, pull the model (ollama pull <model>) or fix the model name.","Verify base_url path: OpenAI-compatible servers usually need /v1 suffix.","Restart or inspect the local server logs if 500/503."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"import aiohttp\n\nasync def server_ok(base_url: str) -> bool:\n    try:\n        async with aiohttp.ClientSession() as s, s.get(base_url + '/models') as r:\n            return r.status == 200\n    except aiohttp.ClientError:\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    resp = await provider.complete(prompt)\nexcept LLMAPIError as e:\n    if e.status_code == 404:\n        fix_model_and_retry()\n    elif e.status_code >= 500:\n        await asyncio.sleep(2); retry()","preventionTips":["Pre-flight check that the requested model exists via /models","Wrap non-streaming calls with the same retry policy as streaming","Log status_code and body together for postmortems"],"tags":["http-error","local-llm","upstream"],"backgroundTag":"llm-api-http-error","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}