diff --git a/litellm/integrations/datadog/datadog_llm_obs.py b/litellm/integrations/datadog/datadog_llm_obs.py index 664a0c75f80..8a85cca2254 100644 --- a/litellm/integrations/datadog/datadog_llm_obs.py +++ b/litellm/integrations/datadog/datadog_llm_obs.py @@ -57,7 +57,7 @@ def _parse_tool_call_arguments(raw: object) -> object: return raw try: return json.loads(raw) - except json.JSONDecodeError: + except (RecursionError, json.JSONDecodeError): return raw diff --git a/tests/test_litellm/integrations/datadog/test_datadog_llm_obs_message_schema.py b/tests/test_litellm/integrations/datadog/test_datadog_llm_obs_message_schema.py index 0661a79687d..bd91ad8a8fe 100644 --- a/tests/test_litellm/integrations/datadog/test_datadog_llm_obs_message_schema.py +++ b/tests/test_litellm/integrations/datadog/test_datadog_llm_obs_message_schema.py @@ -366,3 +366,14 @@ class TestDataDogLLMObsCacheTokenMetrics: assert "cache_write_input_tokens" not in metrics assert "non_cached_input_tokens" not in metrics assert metrics["input_tokens"] == 20.0 + + +def test_parse_tool_call_arguments_survives_deeply_nested_json(): + """A hostile/degenerate arguments string must fall back to the raw + string, not raise RecursionError and drop the span.""" + from litellm.integrations.datadog.datadog_llm_obs import ( + _parse_tool_call_arguments, + ) + + hostile = "[" * 50000 + assert _parse_tool_call_arguments(hostile) == hostile