{"record":{"id":"0e9a7dbc06667d7d","repo":"HKUDS/DeepTutor","slug":"local-llm-stream-error-error-text","errorCode":null,"errorMessage":"Local LLM stream error: {error_text}","messagePattern":"Local LLM stream error: (.+?)","errorType":"http","errorClass":"LLMAPIError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/llm/local_provider.py","lineNumber":303,"sourceCode":"        \"temperature\": kwargs.get(\"temperature\", 0.7),\n        \"stream\": True,\n    }\n\n    if kwargs.get(\"max_tokens\"):\n        data[\"max_tokens\"] = kwargs[\"max_tokens\"]\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    try:\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 stream error: {error_text}\",\n                        status_code=response.status,\n                        provider=\"local\",\n                    )\n\n                thinking_parser = _ThinkingBlockParser()\n\n                async for line in response.content:\n                    line_str = line.decode(\"utf-8\").strip()\n\n                    # Skip empty lines\n                    if not line_str:\n                        continue\n\n                    # Handle SSE format\n                    if line_str.startswith(\"data:\"):\n                        data_str = line_str[5:].strip()\n","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/llm/local_provider.py#L285-L321","documentation":"The streaming request to the local LLM server returned a non-200 status; LLMAPIError carries the status code and the raw body so the caller can see why the server refused the stream.","triggerScenarios":"session.post(url) inside stream() returns non-200: wrong model name (404), context length exceeded (400), server error (500), or unsupported streaming flag.","commonSituations":"Model not loaded in Ollama; local server that doesn't support stream=true; oversized prompt exceeding local context window.","solutions":["Inspect the embedded error_text for the server's own message.","Pull/fix the model name; verify the server supports SSE streaming.","Reduce prompt size if the server reports context overflow."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"import aiohttp\n\nasync def model_available(base_url: str, model: str) -> bool:\n    async with aiohttp.ClientSession() as s, s.get(f'{base_url}/models') as r:\n        models = (await r.json()).get('data', [])\n        return any(m.get('id') == model for m in models)","typeGuard":null,"tryCatchPattern":"try:\n    async for chunk in provider.stream(messages): ...\nexcept LLMAPIError as e:\n    if e.status_code == 400:\n        shrink_context_and_retry()\n    elif e.status_code >= 500:\n        await backoff_and_retry()","preventionTips":["Verify model support for stream=true before configuring","Cap prompt size to the local model's context window","Monitor local server memory to preempt OOM 500s"],"tags":["http-error","local-llm","streaming","upstream"],"backgroundTag":"llm-api-http-error","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}