langchain - ✅(Solved) Fix RuntimeError in _AsyncHttpxClientWrapper.__del__ when event loop is closed [4 pull requests, 3 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
langchain-ai/langchain#35327Fetched 2026-04-08 00:26:39
View on GitHub
Comments
3
Participants
3
Timeline
18
Reactions
0
Timeline (top)
labeled ×6cross-referenced ×4referenced ×4commented ×3

Description:

I found a resource cleanup issue in langchain-openai and langchain-anthropic. The _AsyncHttpxClientWrapper.__del__ method attempts to schedule an async cleanup task using asyncio.get_running_loop().

However, if the object is garbage collected during interpreter shutdown or in a context where the event loop is already closed, get_running_loop() raises a RuntimeError.

The current code catches this in a broad except Exception: block and suppresses it, meaning self.aclose() is never called, potentially leading to unclosed resource warnings from httpx

Proposed Fix: Safely check for the running loop using try/except RuntimeError and loop.is_running() before attempting to schedule the cleanup task.

Error Message

import asyncio import gc from langchain_openai.chat_models._client_utils import _AsyncHttpxClientWrapper

async def main(): client = _AsyncHttpxClientWrapper(base_url="http://test", timeout=10) # used... pass

if name == "main": asyncio.run(main()) # Logic: loop is closed here gc.collect() # Trigger del -> RuntimeError: no running event loop

Root Cause

Description:

I found a resource cleanup issue in langchain-openai and langchain-anthropic. The _AsyncHttpxClientWrapper.__del__ method attempts to schedule an async cleanup task using asyncio.get_running_loop().

However, if the object is garbage collected during interpreter shutdown or in a context where the event loop is already closed, get_running_loop() raises a RuntimeError.

The current code catches this in a broad except Exception: block and suppresses it, meaning self.aclose() is never called, potentially leading to unclosed resource warnings from httpx

Proposed Fix: Safely check for the running loop using try/except RuntimeError and loop.is_running() before attempting to schedule the cleanup task.

Fix Action

Fix / Workaround

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
  • This is not related to the langchain-community package.
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

PR fix notes

PR #35337: fix(openai,anthropic): safely handle missing event loop in async client cleanup

Description (problem / solution / changelog)

Fixes #35327

This PR fixes a resource cleanup bug in _AsyncHttpxClientWrapper.__del__ where a RuntimeError ("no running event loop") was raised and silently suppressed if the object was garbage collected when no event loop was running

(ex - during interpreter shutdown).

This affects both langchain-openai and langchain-anthropic usage.

I have modified the del method to safely check for a running loop using try/except RuntimeError and loop.is_running() before attempting to schedule the aclose() cleanup task.

Tested:

I created a local reproduction script (verify_fix.py) that simulates the object lifecycle in two scenarios:

  • With a running loop: Confirmed that aclose() is correctly scheduled and called.
  • Without a running loop: Confirmed that del completes without raising or logging any exceptions (graceful exit).

Changed files

  • libs/partners/anthropic/langchain_anthropic/_client_utils.py (modified, +13/-4)
  • libs/partners/openai/langchain_openai/chat_models/_client_utils.py (modified, +13/-4)

PR #35352: fix(openai, anthropic): handle closed event loop in _AsyncHttpxClientWrapper.del

Description (problem / solution / changelog)

Summary

Fixes #35327

_AsyncHttpxClientWrapper.__del__ in both langchain-openai and langchain-anthropic attempts to schedule aclose() via asyncio.get_running_loop().create_task(...). When no event loop is running (e.g. after asyncio.run() completes and the object is garbage-collected), get_running_loop() raises RuntimeError. The existing broad except Exception catches this silently, but the underlying httpx transport is never closed, leading to resource leak warnings.

Changes

  • Added a RuntimeError-specific except clause in __del__ for both langchain-openai and langchain-anthropic
  • When no running loop is available, a temporary event loop is created to properly run aclose() and clean up the transport
  • The broad except Exception fallback is preserved for any other unexpected errors
  • Added regression tests in both packages

Why this approach

The fix creates a fresh event loop as a fallback only when the primary path (get_running_loop().create_task()) fails with RuntimeError. This ensures the httpx transport is actually cleaned up rather than silently leaked. The temporary loop is immediately closed after use, so it does not interfere with the application's event loop lifecycle.

Test plan

  • Added test_async_client_del_no_running_loop for langchain-openai
  • Added test_async_client_del_no_running_loop for langchain-anthropic
  • Existing unit tests pass for both packages

This PR was authored with AI assistance (Claude Code).

Changed files

  • libs/partners/anthropic/langchain_anthropic/_client_utils.py (modified, +9/-0)
  • libs/partners/anthropic/tests/unit_tests/test_client_utils.py (modified, +20/-0)
  • libs/partners/openai/langchain_openai/chat_models/_client_utils.py (modified, +9/-0)
  • libs/partners/openai/tests/unit_tests/chat_models/test_client_utils.py (added, +23/-0)

PR #35353: fix: handle closed event loop in _AsyncHttpxClientWrapper.del

Description (problem / solution / changelog)

Summary

  • Fix RuntimeError in _AsyncHttpxClientWrapper.__del__ when the event loop is already closed (e.g. after asyncio.run() completes)
  • When asyncio.get_running_loop() raises RuntimeError, fall back to creating a temporary event loop via asyncio.new_event_loop() to properly close the underlying httpx transport
  • Applied the same fix to both langchain-openai and langchain-anthropic packages

Issue

Fixes #35327

When _AsyncHttpxClientWrapper is garbage collected after the event loop has been closed, asyncio.get_running_loop() raises RuntimeError. The previous code caught this via a broad except Exception but never actually closed the httpx client, leading to unclosed resource warnings.

The fix catches RuntimeError specifically and creates a temporary event loop to run aclose() synchronously, ensuring the transport is properly cleaned up.

Test plan

  • Added test_async_client_del_no_running_loop — verifies no exception is raised when GC runs after event loop is closed
  • Added test_async_client_del_closes_transport — verifies the client is actually closed (not just silently leaked)
  • Tests added for both langchain-openai and langchain-anthropic
  • All existing tests continue to pass

Changed files

  • libs/partners/anthropic/langchain_anthropic/_client_utils.py (modified, +9/-0)
  • libs/partners/anthropic/tests/unit_tests/test_client_utils.py (modified, +36/-0)
  • libs/partners/openai/langchain_openai/chat_models/_client_utils.py (modified, +9/-0)
  • libs/partners/openai/tests/unit_tests/chat_models/test_client_utils.py (added, +39/-0)

PR #35878: fix: handle closed event loop in _AsyncHttpxClientWrapper.del

Description (problem / solution / changelog)

Summary

Fixes #35327

The _AsyncHttpxClientWrapper.__del__ method in both langchain-openai and langchain-anthropic raises a RuntimeError when the event loop is already closed (e.g., after asyncio.run() completes and garbage collection triggers __del__).

Changes

  • Replace broad except Exception with specific except RuntimeError — only catch the expected error from asyncio.get_running_loop() when no loop is running
  • Add loop.is_closed() guard — before calling loop.create_task(self.aclose()), verify the loop is still open
  • Applied to both langchain-openai (libs/partners/openai/langchain_openai/chat_models/_client_utils.py) and langchain-anthropic (libs/partners/anthropic/langchain_anthropic/_client_utils.py)

Before

try:
    asyncio.get_running_loop().create_task(self.aclose())
except Exception:
    pass

After

try:
    loop = asyncio.get_running_loop()
    if not loop.is_closed():
        loop.create_task(self.aclose())
except RuntimeError:
    # No running event loop — cannot schedule async cleanup.
    # Expected during interpreter shutdown or after asyncio.run().
    pass

Test plan

  • Reproduce the issue using the reproduction script from #35327
  • Verify no RuntimeError after asyncio.run() completes and gc.collect() triggers __del__
  • Verify async cleanup still works when event loop is running

🤖 Generated with Claude Code

Changed files

  • libs/partners/anthropic/langchain_anthropic/_client_utils.py (modified, +7/-3)
  • libs/partners/openai/langchain_openai/chat_models/_client_utils.py (modified, +7/-3)

Code Example

import asyncio
import gc
from langchain_openai.chat_models._client_utils import _AsyncHttpxClientWrapper

async def main():
    client = _AsyncHttpxClientWrapper(base_url="http://test", timeout=10)
    # used...
    pass

if __name__ == "__main__":
    asyncio.run(main()) 
    # Logic: loop is closed here
    gc.collect() # Trigger __del__ -> RuntimeError: no running event loop

---
RAW_BUFFERClick to expand / collapse

Checked other resources

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
  • This is not related to the langchain-community package.
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

Package (Required)

  • langchain
  • langchain-openai
  • langchain-anthropic
  • langchain-classic
  • langchain-core
  • langchain-model-profiles
  • langchain-tests
  • langchain-text-splitters
  • langchain-chroma
  • langchain-deepseek
  • langchain-exa
  • langchain-fireworks
  • langchain-groq
  • langchain-huggingface
  • langchain-mistralai
  • langchain-nomic
  • langchain-ollama
  • langchain-openrouter
  • langchain-perplexity
  • langchain-qdrant
  • langchain-xai
  • Other / not sure / general

Related Issues / PRs

No response

Reproduction Steps / Example Code (Python)

import asyncio
import gc
from langchain_openai.chat_models._client_utils import _AsyncHttpxClientWrapper

async def main():
    client = _AsyncHttpxClientWrapper(base_url="http://test", timeout=10)
    # used...
    pass

if __name__ == "__main__":
    asyncio.run(main()) 
    # Logic: loop is closed here
    gc.collect() # Trigger __del__ -> RuntimeError: no running event loop

Error Message and Stack Trace (if applicable)

Description

Description:

I found a resource cleanup issue in langchain-openai and langchain-anthropic. The _AsyncHttpxClientWrapper.__del__ method attempts to schedule an async cleanup task using asyncio.get_running_loop().

However, if the object is garbage collected during interpreter shutdown or in a context where the event loop is already closed, get_running_loop() raises a RuntimeError.

The current code catches this in a broad except Exception: block and suppresses it, meaning self.aclose() is never called, potentially leading to unclosed resource warnings from httpx

Proposed Fix: Safely check for the running loop using try/except RuntimeError and loop.is_running() before attempting to schedule the cleanup task.

System Info

System Information

OS: Darwin OS Version: Darwin Kernel Version 25.2.0: Tue Nov 18 21:08:48 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T8132 Python Version: 3.13.0rc3 (v3.13.0rc3:fae84c70fbd, Oct 1 2024, 00:10:10) [Clang 15.0.0 (clang-1500.3.9.4)]

Package Information

langchain_core: 1.2.13 langsmith: 0.7.4 langgraph_sdk: 0.3.6

Other Dependencies

httpx: 0.28.1

extent analysis

Fix Plan

Safely Check for Running Loop

To fix the issue, we need to safely check for the running loop before attempting to schedule the cleanup task. We can do this by using a try-except block to catch the RuntimeError exception and also check if the loop is running using loop.is_running().

import asyncio

class _AsyncHttpxClientWrapper:
    def __del__(self):
        try:
            loop = asyncio.get_running_loop()
            if loop.is_running():
                loop.create_task(self.aclose())
        except RuntimeError:
            # If the loop is not running, we can't schedule a task to close it
            # In this case, we can simply close the client synchronously
            self.aclose()

Update Existing Code

We need to update the existing code to use the new _AsyncHttpxClientWrapper class.

import asyncio
from langchain_openai.chat_models._client_utils import _AsyncHttpxClientWrapper

async def main():
    client = _AsyncHttpxClientWrapper(base_url="http://test", timeout=10)
    # used...
    pass

if __name__ == "__main__":
    asyncio.run(main()) 
    # Logic: loop is closed here
    gc.collect() # Trigger __del__ -> No more RuntimeError

Verification

To verify that the fix worked, we can run the code again and check if the RuntimeError exception is raised. We can also use a debugger to step through the code and see if the cleanup task is scheduled correctly.

Extra Tips

  • Always use a try-except block to catch exceptions and handle them safely.
  • Use loop.is_running() to check if the loop is running before attempting to schedule a task.
  • Consider using a more robust way to handle cleanup tasks, such as using a context manager.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

langchain - ✅(Solved) Fix RuntimeError in _AsyncHttpxClientWrapper.__del__ when event loop is closed [4 pull requests, 3 comments, 3 participants]