{"record":{"id":"bda8fb47244fd76c","repo":"BerriAI/litellm","slug":"response-read","errorCode":null,"errorMessage":"{response.read()}","messagePattern":"\\{response\\.read\\(\\)\\}","errorType":"exception","errorClass":"LangGraphError","httpStatus":null,"severity":"error","filePath":"litellm/llms/langgraph/chat/transformation.py","lineNumber":312,"sourceCode":"        )\n        from litellm.utils import CustomStreamWrapper\n\n        if client is None or not isinstance(client, HTTPHandler):\n            client = _get_httpx_client(params={})\n\n        verbose_logger.debug(\"Making sync streaming request to: %s\", api_base)\n\n        # Make streaming request\n        response: Final = client.post(\n            api_base,\n            headers=headers,\n            data=json.dumps(data),\n            stream=True,\n            logging_obj=logging_obj,\n        )\n\n        if response.status_code != 200:\n            raise LangGraphError(status_code=response.status_code, message=str(response.read()))\n\n        # Create iterator for SSE stream\n        completion_stream: Final = self.get_streaming_response(model=model, raw_response=response)\n\n        streaming_response: Final = CustomStreamWrapper(\n            completion_stream=completion_stream,\n            model=model,\n            custom_llm_provider=custom_llm_provider,\n            logging_obj=logging_obj,\n        )\n\n        # LOGGING\n        logging_obj.post_call(\n            input=messages,\n            api_key=\"\",\n            original_response=\"first stream response received\",\n            additional_args={\"complete_input_dict\": data},\n        )","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/llms/langgraph/chat/transformation.py#L294-L330","documentation":"On the synchronous streaming path, LangGraphConfig POSTs to {api_base}/runs/stream and any non-200 response is turned into LangGraphError whose message is the raw response body (str(response.read())). The status code and body come from the LangGraph server itself, so they carry the real failure reason (auth, unknown graph, payload validation).","triggerScenarios":"litellm.completion(..., stream=True) against a langgraph model where the server answers non-200: 404 when api_base is not a LangGraph API server, 401/403 with a missing/invalid API key, 422 when the payload names a graph/assistant that does not exist, 409 on thread conflicts, 5xx on server crashes.","commonSituations":"Pointing at a LangGraph Platform/Agent Server deployment with the wrong path prefix; expired API key; assistant/graph not yet deployed; local dev server not running the expected graph.","solutions":["Inspect e.status_code and e.message (the server's response body) for the concrete reason","401/403: set the correct API key (api_key kwarg or provider env var)","404: fix api_base so it is the LangGraph API root (it appends /runs/stream itself)","422: verify the assistant/graph identifier in the model name exists on the server","Reproduce outside litellm: curl -X POST {api_base}/runs/wait -H 'Content-Type: application/json' -d '{...}'"],"exampleFix":"# before: api_base points at a UI route, streaming POST returns 404\nlitellm.completion(model=\"langgraph/agent\", api_base=\"https://host/ui\", stream=True, messages=[...])\n\n# after\ntry:\n    litellm.completion(model=\"langgraph/agent\", api_base=\"https://host\", stream=True, messages=[...])\nexcept litellm.llms.langgraph.chat.transformation.LangGraphError as e:\n    log.error(\"langgraph stream failed: %s %s\", e.status_code, e.message)","handlingStrategy":"try-catch","validationCode":"import httpx\n\ndef langgraph_stream_reachable(base: str, api_key: str | None) -> None:\n    r = httpx.post(\n        f\"{base.rstrip('/')}/runs/wait\",\n        headers={\"Authorization\": f\"Bearer {api_key}\"} if api_key else {},\n        json={\"assistant_id\": \"agent\", \"input\": {\"messages\": []}},\n        timeout=10,\n    )\n    if r.status_code in (401, 403, 404):\n        raise RuntimeError(f\"LangGraph server rejected probe: {r.status_code} {r.text[:200]}\")","typeGuard":null,"tryCatchPattern":"try:\n    stream = litellm.completion(model=\"langgraph/agent\", stream=True, messages=msgs, api_base=base)\nexcept Exception as e:\n    status = getattr(e, \"status_code\", None)\n    if status and 500 <= status < 600 and attempt < MAX_RETRIES:\n        time.sleep(2 ** attempt)  # transient server error: retry with backoff\n        continue\n    log.error(\"LangGraph stream failed: HTTP %s body=%s\", status, getattr(e, \"message\", e))\n    raise","preventionTips":["Log e.status_code and e.message together — the body carries the server's real reason","Validate api_base and key with one cheap /runs/wait probe at startup","Retry only transient statuses (429/5xx); surface 4xx immediately as config bugs"],"tags":["langgraph","streaming","http-status","upstream-error"],"backgroundTag":"upstream-http-error","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}