fix(errors): only link bug reports for exceptions without a provider status

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
ryan 2026-09-21 17:51:41 +00:00
parent 2fb4391232
commit 33ff50eb11
7 changed files with 54 additions and 10 deletions

View file

@ -43,6 +43,10 @@ def bug_report_enabled() -> bool:
return os.getenv(DISABLE_ENV_VAR, "").lower() != "true"
def should_report_bug(exc: object) -> bool:
return bug_report_enabled() and isinstance(exc, BaseException) and getattr(exc, "status_code", None) is None
def _format_frame(frame: traceback.FrameSummary, package_dir: Path, package_parent: Path) -> str | None:
frame_path: Final = Path(frame.filename).resolve()
try:

View file

@ -11,9 +11,9 @@ import httpx
import litellm
from litellm._logging import _ENABLE_SECRET_REDACTION, _redact_string, verbose_logger
from litellm.litellm_core_utils.bug_report import (
bug_report_enabled,
bug_report_notice,
build_bug_report,
should_report_bug,
)
from litellm.litellm_core_utils.secret_redaction import redact_string
from litellm.types.utils import LlmProviders
@ -2690,7 +2690,7 @@ def exception_type(
custom_llm_provider=cast(str | None, custom_llm_provider),
)
)
if bug_report_enabled() and isinstance(original_exception, BaseException)
if should_report_bug(original_exception)
else ""
)
),

View file

@ -47,9 +47,9 @@ from litellm.constants import (
)
from litellm.integrations.custom_guardrail import CustomGuardrail
from litellm.litellm_core_utils.bug_report import (
bug_report_enabled,
bug_report_notice,
build_bug_report,
should_report_bug,
strip_bug_report_notice,
)
from litellm.litellm_core_utils.core_helpers import (
@ -3665,7 +3665,7 @@ class ProxyBaseLLMRequestProcessing:
_code = _exc_status_code
else:
_code = status.HTTP_500_INTERNAL_SERVER_ERROR
if bug_report_enabled():
if should_report_bug(e):
proxy_server_request: Final = self.data.get("proxy_server_request")
request_url: Final = (
proxy_server_request.get("url") if isinstance(proxy_server_request, Mapping) else None

View file

@ -75,9 +75,9 @@ from litellm.constants import (
)
from litellm.litellm_core_utils.asyncify import asyncify
from litellm.litellm_core_utils.bug_report import (
bug_report_enabled,
bug_report_notice,
build_bug_report,
should_report_bug,
)
from litellm.litellm_core_utils.litellm_logging import (
_init_custom_logger_compatible_class,
@ -1875,7 +1875,7 @@ async def otel_unhandled_exception_handler(request: Request, exc: Exception):
if isinstance(exc, (ProxyException, HTTPException, RequestValidationError)):
raise exc
verbose_proxy_logger.exception("Unhandled exception in request: %s", type(exc).__name__)
if bug_report_enabled():
if should_report_bug(exc):
verbose_proxy_logger.error(
bug_report_notice(build_bug_report(exc, surface="proxy", call_type=request.url.path))
)

View file

@ -56,9 +56,9 @@ from litellm.constants import (
SPEND_LOG_WRITE_BATCH_MAX_ROWS,
)
from litellm.litellm_core_utils.bug_report import (
bug_report_enabled,
bug_report_notice,
build_bug_report,
should_report_bug,
strip_bug_report_notice,
)
from litellm.proxy._types import (
@ -7842,7 +7842,7 @@ def handle_exception_on_proxy(e: Exception, litellm_call_id: str | None = None)
elif isinstance(e, ProxyException):
return with_litellm_call_id(e, litellm_call_id)
_status_code: Final = getattr(e, "status_code", status.HTTP_500_INTERNAL_SERVER_ERROR)
if _status_code == status.HTTP_500_INTERNAL_SERVER_ERROR and bug_report_enabled() and isinstance(e, BaseException):
if should_report_bug(e):
verbose_proxy_logger.error(bug_report_notice(build_bug_report(e, surface="proxy")))
return ProxyException(
message=strip_bug_report_notice(str(e)),

View file

@ -3,10 +3,11 @@ from __future__ import annotations
from typing import cast
from urllib.parse import parse_qs, urlparse
import httpx
import pytest
from litellm._version import version
from litellm.exceptions import BadRequestError
from litellm.exceptions import APIConnectionError, BadRequestError, InternalServerError
from litellm.litellm_core_utils.bug_report import (
DISABLE_ENV_VAR,
ISSUE_URL_BASE,
@ -15,6 +16,7 @@ from litellm.litellm_core_utils.bug_report import (
bug_report_issue_url,
bug_report_notice,
build_bug_report,
should_report_bug,
strip_bug_report_notice,
)
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
@ -81,6 +83,29 @@ def test_bug_report_can_be_disabled(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setenv(DISABLE_ENV_VAR, "true")
assert bug_report_enabled() is False
assert should_report_bug(RuntimeError("boom")) is False
@pytest.mark.parametrize(
"exc",
[
InternalServerError(message="upstream 500", llm_provider="openai", model="gpt-4"),
APIConnectionError(
message="connection reset",
llm_provider="openai",
model="gpt-4",
request=httpx.Request(method="POST", url="https://api.openai.com/v1/"),
),
BadRequestError(message="bad input", llm_provider="openai", model="gpt-4"),
"not an exception",
],
)
def test_should_report_bug_skips_provider_and_network_errors(exc: object):
assert should_report_bug(exc) is False
def test_should_report_bug_accepts_plain_python_errors():
assert should_report_bug(KeyError("missing")) is True
def test_proxy_provider_uses_translation_domain():

View file

@ -6,10 +6,12 @@ import pytest
from fastapi import HTTPException
from litellm.caching.caching import DualCache
from litellm.exceptions import InternalServerError
from litellm.integrations.custom_guardrail import CustomGuardrail
from litellm.integrations.custom_logger import CustomLogger
from litellm.litellm_core_utils.bug_report import ISSUE_URL_BASE
from litellm.proxy._types import ProxyErrorTypes, UserAPIKeyAuth
from litellm.proxy.utils import PrismaClient, ProxyLogging
from litellm.proxy.utils import PrismaClient, ProxyLogging, handle_exception_on_proxy
from litellm.types.guardrails import GuardrailEventHooks
@ -2418,3 +2420,16 @@ def test_mcp_auth_policy_uses_original_request_model(monkeypatch, model, expecte
synthetic = proxy_logging._convert_mcp_to_llm_format(proxy_logging._create_mcp_request_object_from_kwargs(kwargs), kwargs)
assert ("model-rule" in synthetic["metadata"]["guardrails"]) is expected
assert "request-rule" in synthetic["metadata"]["guardrails"]
def test_handle_exception_on_proxy_logs_bug_report_only_for_unmapped_500(caplog):
with caplog.at_level("ERROR", logger="LiteLLM Proxy"):
provider_result = handle_exception_on_proxy(
InternalServerError(message="upstream 500", llm_provider="openai", model="gpt-4")
)
assert ISSUE_URL_BASE not in caplog.text
internal_result = handle_exception_on_proxy(KeyError("missing"))
assert provider_result.code == internal_result.code == "500"
assert ISSUE_URL_BASE in caplog.text
assert ISSUE_URL_BASE not in internal_result.message