fix(openai): async streaming no-response branch now uses the computed status

Review follow-up: the helper computed 400 for a bare construction-time
OpenAIError, but async_streaming's no-response branch hard-coded 500 and
discarded it -- so with stream=True a missing API key still surfaced as a
retryable server error, the exact class of misreport this PR exists to
fix. Verified at runtime: acompletion(stream=True) with no key raised
InternalServerError(500) before, BadRequestError(400) after.

Adds an end-to-end async-streaming regression test (red without the fix,
green with it); the existing tests covered the helper only, which is why
this slipped through. Transient classes are unaffected:
_status_code_for_openai_sdk_error still returns 500 for anything without
a status_code that is not a bare OpenAIError, and the ReadTimeout->408
branch is untouched.
This commit is contained in:
Ambuj Upadhyay 2026-08-18 22:44:52 +05:30
parent 3dac50ef92
commit 91a3d4ea5e
2 changed files with 31 additions and 1 deletions

View file

@ -1202,8 +1202,14 @@ class OpenAIChatCompletion(BaseLLM, BaseOpenAILLM):
body=exception_body,
)
else:
# status_code was computed above by
# _status_code_for_openai_sdk_error; hard-coding 500
# here made the no-response branch discard it, so an
# async streaming call with no configured API key
# surfaced as a retryable 500 instead of the 400 the
# non-streaming path reports.
raise OpenAIError(
status_code=500,
status_code=status_code,
message=f"{e}",
headers=error_headers,
body=exception_body,

View file

@ -48,3 +48,27 @@ def test_status_code_zero_is_preserved():
status_code = 0
assert _status_code_for_openai_sdk_error(_ZeroStatus()) == 0
@pytest.mark.asyncio
async def test_async_streaming_missing_credentials_is_not_a_server_error(monkeypatch):
"""
The helper alone is not enough: async_streaming's no-response branch used
to hard-code 500, discarding the status computed above it, so the same
missing-key error that maps to 400 on the non-streaming path surfaced as a
retryable 500 when stream=True. This exercises the full call path.
"""
import litellm
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
monkeypatch.setattr(litellm, "api_key", None, raising=False)
monkeypatch.setattr(litellm, "openai_key", None, raising=False)
with pytest.raises(Exception) as exc_info:
await litellm.acompletion(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "hi"}],
stream=True,
)
assert getattr(exc_info.value, "status_code", None) == 400