Skip to content

fix(proxy): extend WARN/ERROR exception routing to LLM hot path - #40

Merged
songkuan-zheng merged 1 commit into
fix/proxy-exception-log-classificationfrom
fix/proxy-llm-hot-path-exception-logging
Jun 3, 2026
Merged

fix(proxy): extend WARN/ERROR exception routing to LLM hot path#40
songkuan-zheng merged 1 commit into
fix/proxy-exception-log-classificationfrom
fix/proxy-llm-hot-path-exception-logging

Conversation

@songkuan-zheng

@songkuan-zheng songkuan-zheng commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Stacked on PR #39 — review after PR #39 merges, or look at this diff against PR #39's branch.

Summary

  • Extends the WARN/ERROR exception-routing policy from PR fix(proxy): classify management-endpoint exceptions into WARN vs ERROR #39 (management endpoints) to the LLM request hot path and the shared utilities that wrap it.
  • 8 files, ~65 catch-all sites migrated; same `log_proxy_exception` helper, same policy: known business / 4xx / upstream-502/503/504 → WARN single structured line, no traceback. Real faults → ERROR + traceback.
  • Audited `proxy_server.py` carefully: 20 sites are route-handler catch-alls (migrated), 25 are startup / config-reload / background-health-check helpers (deliberately left as ERROR — failures there ARE the bug).

Files

File Sites Notes
`proxy_server.py` 20 LLM routes + login + admin reload endpoints + streaming wrappers + realtime websocket
`common_request_processing.py` 10 Stream lifecycle + central `_handle_llm_api_exception`
`utils.py` 4 post_call hooks + email
`auth/auth_checks.py` 4 team/access-group/org lookups
`common_utils/http_parsing_utils.py` 2 Invalid-JSON 400
`auth/user_api_key_auth.py` 2 Websocket auth + missing-header misuse fix
`route_llm_request.py` 1 aiohttp session recreation
`litellm_pre_call_utils.py` 1 api-version query parse

E2E verification (this branch built and deployed to local e2e proxy)

7 distinct 4xx scenarios — all WARN single line, 0 traceback:

Scenario HTTP Log
`POST /v1/chat/completions` unknown model 400 `WARNING exception_logging.py llm_api[handle-exception] failed: Invalid model name`
`POST /v1/moderations` upstream OpenAI 401 401 `WARNING /v1/moderations failed: Incorrect API key provided`
`POST /utils/token_counter` nonexistent model 200 `WARNING /utils/token_counter[get-deployment] failed: BadRequestError` (then succeeds with fallback)
`GET /schedule/model_cost_map_reload/status` no auth 401 `WARNING auth_exception_handler.py user_api_key_auth failed: No api key passed in.`
`POST /v1/chat/completions` bogus bearer 401 `WARNING auth_exception_handler.py user_api_key_auth failed: ProxyException`
`POST /v1/chat/completions` malformed JSON body 400 `WARNING http_parsing_utils.py Invalid JSON payload received`
`POST /login` wrong password 401 `WARNING auth_exception_handler.py user_api_key_auth failed: ...`

Stress test — 50 successive 401s from rotated bogus bearer tokens:

```
ERROR=0 WARNING=57 Traceback=0
```

(57 = 7 above + 50 stress.)

Pre-existing 79 tests in `test_auth_exception_handler.py` + `test_exception_logging.py` still pass — `RuntimeError` / `ValueError` / no-status-code exceptions still route to ERROR + traceback.

Test plan

  • `uv run black` clean (auto-applied)
  • All 8 modified files import-sanity OK
  • 79 existing exception-logging unit tests pass
  • E2E: 7 scenarios + 50-req stress → 0 ERROR / 0 Traceback / 57 WARN
  • Live verify after merge + bump: real production traffic should show LLM-route ERROR-stream noise drop further (after PR fix(proxy): classify management-endpoint exceptions into WARN vs ERROR #39 we cleaned management endpoints; this finishes the request-handling surface)

PR #39 covered management endpoints; this finishes the job for the
LLM request path and shared utilities that wrap it. Same policy: known
business exceptions (4xx with stable type, upstream 502/503/504) log
at WARN with a single structured line; truly unexpected exceptions
(bare RuntimeError, KeyError, real 500) keep ERROR + traceback.

Scope:

- proxy_server.py: 20 route handlers — completion, moderations,
  audio_transcriptions, realtime websocket (pre-call / upstream-status
  / internal-error), token_counter, login_v2/v3/exchange,
  async_data_generator + async_assistants_data_generator streaming
  wrappers, plus 9 reload/schedule/cancel/status admin endpoints for
  model_cost_map and anthropic_beta_headers.

  Deliberately NOT migrated: 25 startup / background-health-check /
  config-reload / model-list-filter helpers. Those run outside the
  request path, and failures there ARE the bug — they should keep
  ERROR + traceback.

- common_request_processing.py: stream first-chunk consumer,
  deferred-logging hooks, post-call streaming guardrail, orphaned
  streaming logging, async_data_generator wrapper, and the central
  _handle_llm_api_exception classifier.

- route_llm_request.py: shared aiohttp session recreation fallback —
  failure is non-fatal (we fall back to None and continue), shouldn't
  flood ERROR.

- litellm_pre_call_utils.py: api-version query-param parse.

- utils.py: post_call_failure_hook callback failures (both inner and
  setup), post_call_response_headers_hook, send_email.

- auth/user_api_key_auth.py: websocket auth wrapper (was logging via
  the noisy code path). Also fixes a misuse of verbose_proxy_logger
  .exception() outside an except block (custom_litellm_key_header
  branch) — there's no current exception, so demoted to a single
  WARNING line that no longer prints "(NoneType: None)".

- auth/auth_checks.py: get_team_membership, access_group lookup, team
  alias lookup, organization alias lookup. All four were ERROR +
  traceback for routine 404 outcomes.

- common_utils/http_parsing_utils.py: invalid-JSON 400 was logged at
  ERROR (no traceback, but still polluted the ERROR stream on every
  malformed client payload); demoted to WARN. The unexpected-exception
  catch-all stays ERROR + traceback — that's the path that catches
  real server faults.

E2E verification on this branch:
- 7 distinct 4xx scenarios (unknown model / bad provider key /
  unauthenticated /schedule/* / bogus bearer / malformed JSON body /
  bogus login) → all WARN single line, 0 traceback.
- 50 successive 401s from rotated bogus bearer tokens → 50 WARN,
  0 ERROR, 0 traceback.
- Pre-existing 79 unit tests in test_auth_exception_handler +
  test_exception_logging still pass (RuntimeError/ValueError still
  route to ERROR + traceback).
@songkuan-zheng
songkuan-zheng merged commit 1b7f381 into fix/proxy-exception-log-classification Jun 3, 2026
@songkuan-zheng
songkuan-zheng deleted the fix/proxy-llm-hot-path-exception-logging branch June 3, 2026 10:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant