Skip to content

Conversation

@AlexsanderHamir
Copy link
Collaborator

@AlexsanderHamir AlexsanderHamir commented Oct 28, 2025

Title

fix: prevent httpx DeprecationWarning memory leak in AsyncHTTPHandler

Relevant issues

addresses #12685

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • I have added a screenshot of my new test passing locally
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem

Type

🐛 Bug Fix

Changes

  • Fixed memory leak in AsyncHTTPHandler by routing bytes/str to content= parameter instead of data= when building httpx requests, preventing DeprecationWarning that was leaking memory

Memory Impact

Before

======================================================================
SDK Async Memory Leak Detection Test
======================================================================
Warming up...
Warm-up complete. Starting measured phase...

Batch 1/15: Current=0.72 MB | Peak=0.78 MB
Batch 2/15: Current=0.80 MB | Peak=0.86 MB
Batch 3/15: Current=0.87 MB | Peak=0.93 MB
Batch 4/15: Current=0.95 MB | Peak=1.00 MB
Batch 5/15: Current=1.02 MB | Peak=1.07 MB
Batch 6/15: Current=1.09 MB | Peak=1.14 MB
Batch 7/15: Current=1.16 MB | Peak=1.22 MB
Batch 8/15: Current=1.23 MB | Peak=1.29 MB
Batch 9/15: Current=1.30 MB | Peak=1.36 MB
Batch 10/15: Current=1.37 MB | Peak=1.43 MB
Batch 11/15: Current=1.44 MB | Peak=1.50 MB
Batch 12/15: Current=1.51 MB | Peak=1.57 MB
Batch 13/15: Current=1.58 MB | Peak=1.64 MB
Batch 14/15: Current=1.66 MB | Peak=1.71 MB
Batch 15/15: Current=1.73 MB | Peak=1.78 MB

======================================================================
Memory Growth Analysis
======================================================================
Initial avg: 0.98 MB
Final avg:   1.41 MB
Growth:      0.43 MB (43.6%)
FAILED[FIXTURE] Test done, running teardown...

After

======================================================================
SDK Async Memory Leak Detection Test
======================================================================
Warming up...
Warm-up complete. Starting measured phase...

Batch 1/15: Current=0.54 MB | Peak=1.11 MB
Batch 2/15: Current=0.55 MB | Peak=1.11 MB
Batch 3/15: Current=0.55 MB | Peak=1.11 MB
Batch 4/15: Current=0.55 MB | Peak=1.11 MB
Batch 5/15: Current=0.55 MB | Peak=1.11 MB
Batch 6/15: Current=0.55 MB | Peak=1.11 MB
Batch 7/15: Current=0.55 MB | Peak=1.11 MB
Batch 8/15: Current=0.53 MB | Peak=1.11 MB
Batch 9/15: Current=0.54 MB | Peak=1.11 MB
Batch 10/15: Current=0.54 MB | Peak=1.11 MB
Batch 11/15: Current=0.54 MB | Peak=1.11 MB
Batch 12/15: Current=0.54 MB | Peak=1.11 MB
Batch 13/15: Current=0.54 MB | Peak=1.11 MB
Batch 14/15: Current=0.55 MB | Peak=1.11 MB
Batch 15/15: Current=0.55 MB | Peak=1.11 MB

======================================================================
Memory Growth Analysis
======================================================================
Initial avg: 0.55 MB
Final avg:   0.54 MB
Growth:      -0.01 MB (-1.4%)
Memory stabilized — no leak detected

Leaky Function

The severe memory leak observed in the SDK was caused by an HTTPX deprecation warning function, not by LiteLLM.

    """
    Handles encoding the given `content`, `data`, `files`, and `json`,
    returning a two-tuple of (<headers>, <stream>).
    """
    if data is not None and not isinstance(data, Mapping):
        # We prefer to separate `content=<bytes|str|byte iterator|bytes aiterator>`
        # for raw request content, and `data=<form data>` for url encoded or
        # multipart form content.
        #
        # However for compat with requests, we *do* still support
        # `data=<bytes...>` usages. We deal with that case here, treating it
        # as if `content=<...>` had been supplied instead.
        message = "Use 'content=<...>' to upload raw bytes/text content."
        
        
				<<< leaking memory >>> 
				warnings.warn(message, DeprecationWarning, stacklevel=2)
				<<< leaking memory >>> 
				
				
        return encode_content(data)

Test Results

[
    {
        "request_number": 1,
        "memory_allocations": [
            {
                "filename": "  File \"/Users/alexsandergomes/.pyenv/versions/3.12.10/lib/python3.12/site-packages/httpx/_content.py\", line 206",
                "size_diff_bytes": 624,
                "size_bytes": 624,
                "count_diff": 7,
                "count": 7
            }
        ]
    },
    {
        "request_number": 2,
        "memory_allocations": [
            {
                "filename": "  File \"/Users/alexsandergomes/.pyenv/versions/3.12.10/lib/python3.12/site-packages/httpx/_content.py\", line 206",
                "size_diff_bytes": 552,
                "size_bytes": 1088,
                "count_diff": 6,
                "count": 12
            }
        ]
    },
    {
        "request_number": 3,
        "memory_allocations": [
            {
                "filename": "  File \"/Users/alexsandergomes/.pyenv/versions/3.12.10/lib/python3.12/site-packages/httpx/_content.py\", line 206",
                "size_diff_bytes": 536,
                "size_bytes": 1536,
                "count_diff": 6,
                "count": 17
            }
        ]
    },
    {
        "request_number": 4,
        "memory_allocations": [
            {
                "filename": "  File \"/Users/alexsandergomes/.pyenv/versions/3.12.10/lib/python3.12/site-packages/httpx/_content.py\", line 206",
                "size_diff_bytes": 528,
                "size_bytes": 1976,
                "count_diff": 6,
                "count": 22
            }
        ]
    },
    {
        "request_number": 5,
        "memory_allocations": [
            {
                "filename": "  File \"/Users/alexsandergomes/.pyenv/versions/3.12.10/lib/python3.12/site-packages/httpx/_content.py\", line 206",
                "size_diff_bytes": 520,
                "size_bytes": 2408,
                "count_diff": 6,
                "count": 27
            }
        ]
    }
]

Route bytes/str to content= parameter instead of data= to avoid deprecation warning that causes memory leak
@vercel
Copy link

vercel bot commented Oct 28, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
litellm Ready Ready Preview Comment Oct 29, 2025 3:54pm

Copy link
Contributor

@ishaan-jaff ishaan-jaff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, can we merge after testing passes

Create _prepare_request_data_and_content() helper to DRY up the logic
for routing data/content parameters correctly in httpx requests.

This helper prevents httpx DeprecationWarnings (which cause memory leaks)
by moving bytes/str from data= to content= parameter while keeping
dict/Mapping in data= parameter.

Applied the helper consistently across all HTTP methods in both
AsyncHTTPHandler and HTTPHandler classes:
- post(), put(), patch(), delete()
- single_connection_post_request()

Related to: b850ed1
Replace lowercase tuple[...] with typing.Tuple[...] in http_handler.py
to fix 'TypeError: type object is not subscriptable' on Python 3.8
@openhands-ai
Copy link

openhands-ai bot commented Oct 29, 2025

Looks like there are a few issues preventing this PR from being merged!

  • GitHub Actions are failing:
    • LiteLLM Mock Tests (folder - tests/test_litellm)
    • Helm unit test
    • LiteLLM Linting
    • LiteLLM Linting
    • LiteLLM Mock Tests (folder - tests/test_litellm)

If you'd like me to help, just leave a comment, like

@OpenHands please fix the failing actions on PR #16024 at branch `litellm_sdk_memory_leak`

Feel free to include any additional details that might help me get this PR into a better state.

You can manage your notification settings

@AlexsanderHamir
Copy link
Collaborator Author

LGTM, can we merge after testing passes

@ishaan-jaff I’ve fixed all errors related to my changes. The remaining test failures don’t appear to be related, but please let me know if I’m mistaken.

@ishaan-jaff ishaan-jaff merged commit 4939793 into main Oct 29, 2025
36 of 57 checks passed
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.

3 participants