fix(proxy): populate Exception.args so str(ProxyException) returns message (LIT-3094) (#29015)
Some checks failed
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Has been cancelled
Unit Tests: Security / security (push) Has been cancelled
Unit Tests: Proxy DB Operations / auth-checks (push) Has been cancelled
Unit Tests: Proxy DB Operations / budgets (push) Has been cancelled
Unit Tests: Proxy DB Operations / custom-logging (push) Has been cancelled
Unit Tests: Proxy DB Operations / db-and-spend (push) Has been cancelled
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Has been cancelled
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Has been cancelled
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Has been cancelled
Unit Tests: Proxy DB Operations / key-generation (push) Has been cancelled
Unit Tests: Proxy DB Operations / logging-misc (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-runtime (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-server-core (push) Has been cancelled
Unit Tests: Proxy DB Operations / schema-migration (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-utils (push) Has been cancelled

* fix(proxy): populate Exception.args so str(ProxyException) returns message

Adds super().__init__(self.message) to ProxyException.__init__ so that
str(exc) returns the stored message instead of empty string. Fixes LIT-3094.

* test(proxy): regression tests for ProxyException.args (LIT-3094)

* fix(proxy): populate Exception.args so str(ProxyException) returns message (LIT-3094)

* fix(proxy): clean up unintended drift; keep only ProxyException.args fix (LIT-3094)
This commit is contained in:
oss-agent-shin 2026-05-27 12:28:11 -07:00 committed by GitHub
parent db94050dd7
commit 1fe911d89d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 79 additions and 0 deletions

View file

@ -3651,6 +3651,11 @@ class ProxyException(Exception):
provider_specific_fields: Optional[dict] = None,
):
self.message = str(message)
# Populate Exception.args so str(self) returns the message.
# Without this, logging paths that call str(original_exception)
# (e.g. StandardLoggingPayloadSetup.get_error_information) record an
# empty error_message for ProxyException-based failures. See LIT-3094.
super().__init__(self.message)
self.type = type
self.param = param
self.openai_code = openai_code or code

View file

@ -69,3 +69,77 @@ def test_internal_jobs_user_has_proxy_admin_role():
assert system_user.user_id == "system"
assert system_user.team_id == "system"
assert system_user.team_alias == "system"
# === Regression tests for LIT-3094: ProxyException must populate Exception.args
# so logging integrations using str(exc) record a non-empty error_message. ===
def test_proxy_exception_str_returns_message():
"""str(ProxyException) must return the stored message, not '' (LIT-3094)."""
from litellm.proxy._types import ProxyException
msg = "key not allowed to access model"
exc = ProxyException(message=msg, type="auth_error", param=None, code=401)
assert str(exc) == msg
assert exc.args == (msg,)
assert exc.message == msg
def test_proxy_exception_populates_standard_logging_error_message():
"""The full logging path used by proxy callbacks must capture the message
instead of recording an empty error_message (LIT-3094 report)."""
from litellm.litellm_core_utils.litellm_logging import (
StandardLoggingPayloadSetup,
)
from litellm.proxy._types import ProxyException
msg = "Authentication Error, Invalid proxy server token passed."
exc = ProxyException(message=msg, type="auth_error", param=None, code=401)
info = StandardLoggingPayloadSetup.get_error_information(original_exception=exc)
assert info["error_message"] == msg
assert info["error_class"] == "ProxyException"
assert info["error_code"] == "401"
def test_proxy_exception_to_dict_unchanged():
"""to_dict() shape must remain backwards-compatible after the fix."""
from litellm.proxy._types import ProxyException
exc = ProxyException(
message="boom", type="invalid_request_error", param="model", code=400
)
d = exc.to_dict()
assert d == {
"message": "boom",
"type": "invalid_request_error",
"param": "model",
"code": "400",
}
def test_proxy_exception_routing_code_override_still_works():
"""The 'No healthy deployment available' -> 429 remapping must survive
the super().__init__() addition."""
from litellm.proxy._types import ProxyException
exc = ProxyException(
message="No healthy deployment available for model=foo",
type="router_error",
param=None,
code=500,
)
assert exc.code == "429"
assert str(exc) == "No healthy deployment available for model=foo"
def test_proxy_exception_non_string_message_coerced():
"""Non-string `message` must still be coerced to str via self.message =
str(message), and Exception.args must reflect the coerced value."""
from litellm.proxy._types import ProxyException
exc = ProxyException(message=42, type="x", param=None, code=400)
assert exc.message == "42"
assert str(exc) == "42"
assert exc.args == ("42",)