fix(logging): stop deepcopying results redaction cannot redact - #36638
Conversation
perform_redaction deepcopies the result before inspecting it, but every shape it does not recognize falls through to the placeholder return at the end of that block, so the copy is built and then discarded. Binary and HTTP response bodies land in exactly that case: batch output, file content and audio responses hold an unpicklable `_thread.lock`, so copy.deepcopy raises TypeError The raise lands inside the try in Logging.success_handler that also wraps the callback loop, so the handler body aborts at the redaction call and everything after it is skipped. It surfaces only as "[Non-Blocking] Exception occurred while success logging cannot pickle '_thread.lock' object", which is why it can run unnoticed. The async handler body reaches perform_redaction the same way. Only deployments with message redaction enabled are affected, since perform_redaction runs only when turn_off_message_logging resolves true Deciding redactability before copying fixes the crash as a consequence rather than catching it, and keeps the deepcopy off large batch bodies it was never going to help. Behaviour for every recognized shape is unchanged: the copy still shields the caller's object from in-place redaction Observed on a live gateway with turn_off_message_logging enabled, where every managed-batch output download logged that error; after this change the error no longer appears
Greptile SummaryThe PR avoids deep-copying response shapes that redaction cannot process, preventing lock-bearing binary responses from interrupting success logging
Confidence Score: 5/5The PR appears safe to merge No blocking failure remains
|
| Filename | Overview |
|---|---|
| litellm/litellm_core_utils/redact_messages.py | Adds the early shape gate that returns the existing redaction placeholder before attempting a deep copy |
| tests/test_litellm/litellm_core_utils/test_redact_messages.py | Adds deterministic regression tests for lock-bearing responses and verifies recognized response objects remain copied and redacted |
Reviews (3): Last reviewed commit: "refactor(logging): drop the type-gate co..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
The comment restated what the gate does and carried incident detail that would drift, including a claim about downstream callbacks that the evidence does not support. The rationale belongs in the regression test, which fails if the copy is ever reintroduced ahead of the gate, rather than in prose that can rot silently Also corrects that test's docstring for the same overclaim: the raise aborts the handler body at the redaction call, and what that costs a given deployment was not established
|
Agreed, removed. The rationale now lives in the regression test, which fails if the copy is reintroduced ahead of the gate. |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit b048ce4. Configure here.
mateo-berri
left a comment
There was a problem hiding this comment.
LGTM. Thanks for the contribution!
67635d8
into
BerriAI:litellm_internal_staging
TLDR
Problem this solves:
How it solves it:
User Flow
Before: an admin who has message redaction turned on sees a logging error for every download of a completed batch's results, and that request never reaches wherever they send their logs
LiteLLM.LoggingError: [Non-Blocking] Exception occurred while success logging cannot pickle '_thread.lock' objectfor that requestAfter: the same download produces no error and completes its logging
Relevant issues
Same failure class as #6631, which fixed an unpicklable value reaching
copy.deepcopyin the Langfuse integration. This is the equivalent site in message redaction, which never got the same treatment.Linear ticket
Resolves LIT-5665
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito re-request a review after pushing changes)Delays 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
QA ran two live proxies from clean worktrees, one per commit, each with its own Postgres, message redaction on (
turn_off_message_logging: true), files routed to OpenAI, and real OpenAI and Anthropic calls (models gpt-5.4-nano and claude-haiku-4-5). The lock-bearing shape comes from Bedrock and Vertex batch downloads on the author's gateway; at this repo's current dependency pins OpenAI, Anthropic, and shared-client binary responses all deepcopy cleanly, so the abort itself is shown with the redaction call the failing success handler makes plus the author's gateway logs, and the live-proxy legs prove the surrounding flow and all three unified endpoints are unchangedBefore (02b0ee7)
Redaction of a lock-bearing response body
with the frames naming this path:
success_handler -> redact_message_input_output_from_logging -> perform_redaction -> copy.deepcopyFile content download through a live proxy
curl -sS -X POST 'http://localhost:22513/v1/files?provider=openai' -H 'Authorization: Bearer sk-qa36638' -F purpose=batch -F file=@input.jsonlreturns 200, idfile-7hoXvfK3Af9sfFgU4NxPzmPOST /v1/batches?provider=openaireturnsbatch_6a8385a77ebc8190b1b790214fab7b00, polled tocompleted, outputfile-HETeNHf3FZyKSSQDn5Nigxcurl -sS -D - 'http://localhost:22513/v1/files/file-HETeNHf3FZyKSSQDn5Nigx/content?provider=openai' -H 'Authorization: Bearer sk-qa36638'returns HTTP/1.1 200 OK with the real completion records (22 tokens of real OpenAI output)grep -c -e 'LoggingError' -e 'cannot pickle' proxy.logreturns 0: OpenAI's binary body deepcopies cleanly at this pin, so the abort does not fire on this provider pair; the lock-bearing case above is what abortsUnified endpoints with redaction on
POST /v1/chat/completions(gpt-5.4-nano) returns 200 "Hi there" (chatcmpl-EDzkZabBs9vNcbeFz3f5EgBmkBpdH); spend row 8.65e-06 USD,messagesandresponsestored as{}POST /v1/responses(gpt-5.4-nano) returns 200 (resp_5SsTnawn6skz...); spend row 2.035e-05 USD, redactedPOST /v1/messages(claude-haiku-4-5) returns 200 "Hi there." (msg_011Ce95an1FZfyj6c1JLFuXy); spend row 4.3e-05 USD, redactedAfter (b048ce4)
Redaction of a lock-bearing response body
cannot picklelines and zero ERROR-level linesFile content download through a live proxy
curl -sS -X POST 'http://localhost:31647/v1/files?provider=openai' -H 'Authorization: Bearer sk-qa36638' -F purpose=batch -F file=@qa36638_input.jsonlreturns 200, idfile-H18VTAMLdtdPDwG211Mdcocurl -sS -i 'http://localhost:31647/v1/files/file-H18VTAMLdtdPDwG211Mdco/content?provider=openai' -H 'Authorization: Bearer sk-qa36638'returns HTTP/1.1 200 OK with the 177 jsonl bytes, both timesgrep -n -i -E "LoggingError|cannot pickle|Traceback" proxy.logfinds no matches across the whole runUnified endpoints with redaction on
POST /v1/chat/completions(gpt-5.4-nano) returns 200 "Hi, nice to meet you." (chatcmpl-EDzMWNLzxRnDt3F69DaeQStOWNhQF); spend row 1.47e-05 USD,messagesandresponsestored as{}POST /v1/responses(gpt-5.4-nano) returns 200 "Hi! How can I help you today?" (resp_gm9gIk3L9CEY...); spend row 2.035e-05 USD, redactedPOST /v1/messages(claude-haiku-4-5) returns 200 "Hi! How's it going?" (msg_011Ce93hfjBgqWjZb7EXVh2b); spend row 7.9e-05 USD, redactedQA observations, all pre-existing behavior this PR neither causes nor worsens:
Type
🐛 Bug Fix
Caveats (if any)
message_loggingoffFinal Attestation