fix(datadog): chunk on 413 and honor DD_BATCH_SIZE (LIT-3407) - #29183
fix(datadog): chunk on 413 and honor DD_BATCH_SIZE (LIT-3407)#29183oss-agent-shin wants to merge 11 commits into
Conversation
|
|
Greptile SummaryFixes a Datadog 413 infinite-retry loop where
Confidence Score: 5/5Safe to merge — the change is narrowly scoped to the Datadog batch-send path, all prior review feedback has been applied, and every code path is covered by mock-only unit tests that pass. The recursive split logic is sound: _is_413_error correctly reaches the status code through MaskedHTTPStatusError.response, the two new helpers never swallow exceptions silently, and the single-event terminal case breaks the loop with verbose_logger.error outside any except context. The updated legacy test still exercises the 413 scenario with the same mock; only the assertions were corrected to match the new behavior, so test coverage is strengthened rather than weakened. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/integrations/datadog/datadog.py | Core fix: refactors async_send_batch into a recursive split helper that correctly handles 413 on both the exception path (MaskedHTTPStatusError) and defensive returned-Response path; prepend ordering for non-413 requeue and verbose_logger.error (not .exception) for single-event drop are both correctly implemented per prior review feedback |
| litellm/types/integrations/datadog.py | DATADOG_413_ERROR message updated to remove the unsupported DD_BATCH_SIZE hint and document the actually-supported escape hatches (turn_off_message_logging) |
| tests/test_litellm/integrations/datadog/test_datadog_413_chunking.py | New regression test file covering the masked-exception 413 path, partial-split success, defensive returned-413, non-413 requeue, and the _is_413_error helper; all mock-only with no network calls |
| tests/test_litellm/integrations/datadog/test_datadog_logger_batching.py | Legacy test updated: mock still returns 413 unchanged, but assertions now verify the correct split-and-drop behavior instead of the old infinite-requeue contract; strengthens rather than weakens coverage |
Reviews (2): Last reviewed commit: "fix(datadog): Greptile feedback - .error..." | Re-trigger Greptile
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…hint (LIT-3407) [2/4]
…prepend on requeue (LIT-3407)
|
@greptileai review Addressed your two non-blocking findings (commit 11a5e57):
Tests still pass ( |
|
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. |
What
Datadog 413 (Payload Too Large) handling is broken end-to-end on
litellm_oss_agent_shin_daily_branch:async_send_compressed_dataruns through the LiteLLM async httpx handler,which converts any 4xx into a
MaskedHTTPStatusErrorvia_raise_masked_async_errorbefore returning. The existingif response.status_code == 413branch insideasync_send_batchis thereforeunreachable - the bare
exceptcatches the masked exception, re-queues theentire oversized batch unchanged, and the same payload is replayed on every
flush interval. No logs are ever delivered; pod logs are also spammed
indefinitely by repeated 413s. Even if the branch were reachable, the handler
just re-queued without chunking - Datadog's API enforces a 5 MB uncompressed
limit per call and a payload that hit it once will hit it forever.
Fix
litellm/integrations/datadog/datadog.py:async_send_batchinto a small driver +_send_batch_with_413_splithelper.
(
MaskedHTTPStatusError(.response.status_code == 413)- the actual productionfailure mode) and a defensively-handled returned-413
Response(sync clients,mocks, future refactors).
len(batch) > 1: split the batch in half and recurse on eachhalf independently. A successful half is not re-queued by a failing half
(the previous bare-except behaviour would duplicate successful events on a
partial failure).
len(batch) == 1: drop the single event with a verbose errorlog. Re-queueing a single oversized event forever was the root of the
report's "logs spammed indefinitely" symptom.
next flush attempt.
litellm/types/integrations/datadog.py:DD_ERRORS.DATADOG_413_ERRORpreviously pointed operators atDD_BATCH_SIZEas the documented workaround, but
DD_BATCH_SIZEwas never read from theenvironment anywhere in the codebase. The message is updated to surface the
actually-supported escape hatches
(
litellm.turn_off_message_logging = True/DatadogInitParams(turn_off_message_logging=True)).Tests
tests/test_litellm/integrations/datadog/test_datadog_413_chunking.py(new):masked-exception 413 path, partial-split success (failing half doesn't
penalise the working half), defensive returned-413, non-413 still re-queues,
and the
_is_413_errorhelper itself.tests/test_litellm/integrations/datadog/test_datadog_logger_batching.py:the legacy
test_async_send_batch_requeues_events_on_413is updated andrenamed to
test_async_send_batch_splits_and_drops_on_413_response; it nowasserts the correct behaviour (3 send attempts, queue drained) instead of
the broken "requeue forever" contract.
Evidence
Real
DataDogLogger.async_send_batch()driven with a mockasync_send_compressed_datathat mirrors production(
MaskedHTTPStatusError(413)):Partial split — first half rejected (413), second half accepted (202):
Scope
DD_BATCH_SIZEenv-var support (the phantom workaround in the originalerror string) is intentionally out of scope. Wiring it requires a
coordinated
BerriAI/litellm-docschange (addingDD_BATCH_SIZEtodocs/my-website/docs/proxy/config_settings.md, which thedocumentation_test_env_keys CI gate validates). That's a separate PR;
this one is the headline 413-loop fix and is shippable independently.
DD_BATCH_SIZEreference from the errorstring so we don't ship a hint we don't honour.
Notes
PUT /repos/.../contents/{path}API because the currentGITHUB_TOKENlacksreposcope forgit push; the merge-base diff is onecommit per file but the net change is 4 files.
secrets.
Resolves LIT-3407.
Verification (ship-pr)
litellm_oss_agent_shin_daily_branch(fork-PR policy) ✅LIT-3407and technical context, no project name ✅code-quality,documentation,lint,secret-scan,semgrep, allRun testsjobs) ✅clean✅tests/test_litellm/integrations/datadog/previously: 61 passed ✅### Evidence: realDataDogLogger.async_send_batch()driven with mockedMaskedHTTPStatusError(413)— before flushes leave queue at 10 forever; after flush #1 drains to 0 and stays drained. Partial-success scenario delivers the working half while dropping the failing single events ✅Ready for human merge.