fix(responses): map upstream 4xx on cancel to client error instead of 500 - #32271
Conversation
Greptile SummaryThis PR fixes a bug where Responses API follow-up endpoints (cancel, get, delete) that call
Confidence Score: 5/5Safe to merge — the change is a single boolean expression fix with a dedicated regression test and demonstrated live-proxy evidence. The change is minimal and targeted: one boolean gate touches only the entry condition for the provider-exception-mapping block. All code inside the block (extra_information formatting, provider mappers, fallback APIConnectionError) already handles No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/litellm_core_utils/exception_mapping_utils.py | One-line gate change from if model: to if model or custom_llm_provider: correctly routes follow-up endpoint exceptions (cancel/get/delete) into the provider-specific mapping block instead of falling through to the generic APIConnectionError path. |
| tests/test_litellm/litellm_core_utils/test_exception_mapping_utils.py | New mock-only test exercises the fixed code path: BaseLLMException(status_code=400) with model=None and custom_llm_provider="azure" must now raise litellm.BadRequestError with status 400 instead of the previous APIConnectionError. |
Reviews (1): Last reviewed commit: "fix(responses): map upstream 4xx on canc..." | Re-trigger Greptile
Greptile SummaryThis PR fixes a one-character bug in
Confidence Score: 5/5Minimal, well-scoped bug fix with a direct regression test and live proof-of-fix screenshots — safe to merge. Single-character addition to a guard condition; downstream mappers already handle model=None safely via try/except; new test covers the exact regression with no network calls. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/litellm_core_utils/exception_mapping_utils.py | One-line fix: gates the provider-specific exception-mapping block on model or custom_llm_provider instead of model alone. |
| tests/test_litellm/litellm_core_utils/test_exception_mapping_utils.py | Adds a focused regression test verifying exception_type(model=None, custom_llm_provider='azure', ...) raises litellm.BadRequestError with no network calls. |
Reviews (2): Last reviewed commit: "fix(responses): map upstream 4xx on canc..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Relevant issues
Found while investigating a customer report on Responses API follow-up requests
Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewDelays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Live proxy on localhost:33551 (
azure-responses-test->azure/gpt-4o, api_version 2025-03-01-preview), real Azure OpenAI callsBefore (unmodified staging): cancelling a synchronous response surfaces Azure's 400 as a 500 APIConnectionError
After (this branch): the upstream client error passes through with its real status and message
Cancelling a
background: trueresponse still works (must not regress)with body
{"status":"cancelled","background":true,...}The same passthrough now applies to the other follow-ups; DELETE of an already deleted response returned a 500 before and now returns the upstream 404
Type
🐛 Bug Fix
Changes
POST /v1/responses/{id}/cancelagainst an Azure synchronous (non background) response makes Azure reject with a 400 "Cannot cancel a synchronous response.", but litellm surfaced it as a 500 APIConnectionError. The responses follow-up endpoints (cancel/get/delete, and likewise evals, skills, vector stores, interactions, rag) calllitellm.exception_type(model=None, ...), and the whole provider mapping block inexception_typeis gated onif model:, so every upstream error on those paths, whatever its real status code, fell through to the generic 500 APIConnectionError fallbackThe gate now also enters the mapping when only
custom_llm_provideris known, which is what the per-provider mappers actually dispatch on; none of them dereferencemodelbeyond message formatting. Upstream 4xx errors on follow-up requests now map to the proper litellm exception (BadRequestError for the sync cancel case) with the provider's original message, exactly like completion-style endpoints already doRegression test in
tests/test_litellm/litellm_core_utils/test_exception_mapping_utils.py: an upstream 400 wrapped in aBaseLLMExceptionwithmodel=Noneandcustom_llm_provider="azure"must map tolitellm.BadRequestErrorwith status 400 and the upstream message preserved. It fails on the pre-fix code with APIConnectionError