{"record":{"id":"0bef7c739ac4cdef","repo":"BerriAI/litellm","slug":"response-aread","errorCode":null,"errorMessage":"{response.aread()}","messagePattern":"\\{response\\.aread\\(\\)\\}","errorType":"exception","errorClass":"LangGraphError","httpStatus":null,"severity":"error","filePath":"litellm/llms/langgraph/chat/transformation.py","lineNumber":371,"sourceCode":"        )\n        from litellm.utils import CustomStreamWrapper\n\n        if client is None or not isinstance(client, AsyncHTTPHandler):\n            client = get_async_httpx_client(llm_provider=cast(Any, \"langgraph\"), params={})\n\n        verbose_logger.debug(\"Making async streaming request to: %s\", api_base)\n\n        # Make async streaming request\n        response: Final = await 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(await response.aread()))\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":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/llms/langgraph/chat/transformation.py#L353-L389","documentation":"Async twin of the sync streaming guard: get_async_custom_stream_wrapper POSTs to {api_base}/runs/stream via await client.post(...), and any non-200 response raises LangGraphError with that status code and the raw body (str(await response.aread())). The body is the LangGraph server's own error text.","triggerScenarios":"await litellm.acompletion(model=\"langgraph/...\", stream=True, ...) where the server responds non-200: 404 wrong api_base, 401/403 bad or missing Bearer key, 422 invalid payload or unknown graph/assistant, 5xx server failure.","commonSituations":"Async FastAPI service calling a LangGraph deployment with a rotated/expired token; wrong base URL in an async worker's env; graph name typo in the model string.","solutions":["Read e.status_code and e.message to get the server's own error detail","Fix credentials for 401/403 (pass api_key so it is sent as Authorization: Bearer)","Fix api_base for 404 (must be the API root; /runs/stream is appended)","Validate the assistant/graph id in the model name for 422","Retry transient 5xx with backoff; verify with a direct httpx POST"],"exampleFix":"# before\nstream = await litellm.acompletion(model=\"langgraph/agent\", stream=True, messages=[...])  # LangGraphError\n\n# after\ntry:\n    stream = await litellm.acompletion(\n        model=\"langgraph/agent\", api_base=LANGGRAPH_URL, api_key=LANGGRAPH_KEY, stream=True, messages=[...]\n    )\nexcept Exception as e:\n    status = getattr(e, \"status_code\", None)\n    if status in (429, 500, 502, 503):\n        await asyncio.sleep(2)\n        stream = await litellm.acompletion(model=\"langgraph/agent\", stream=True, messages=[...])\n    else:\n        raise","handlingStrategy":"try-catch","validationCode":"import httpx\n\nasync def langgraph_ok(base: str, api_key: str | None) -> bool:\n    try:\n        r = await httpx.AsyncClient().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        return r.status_code < 400\n    except httpx.HTTPError:\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    stream = await litellm.acompletion(model=\"langgraph/agent\", stream=True, messages=msgs, api_base=base)\nexcept Exception as e:\n    status = getattr(e, \"status_code\", None)\n    if status in (429, 500, 502, 503) and attempt < MAX_RETRIES:\n        await asyncio.sleep(2 ** attempt)\n        continue\n    log.error(\"LangGraph async stream failed: %s %s\", status, getattr(e, \"message\", e))\n    raise","preventionTips":["Wrap async streaming calls with status-aware retry for 429/5xx only","Keep Authorization tokens fresh; expired keys are the most common 401 on this path","Probe /runs/wait once during service warmup so config errors surface before user traffic"],"tags":["langgraph","streaming","async","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"}