6139dacef9 fix(langgraph): cleanup orphaned waiter task in AsyncPregelLoop (#6167)
### Summary

This PR fixes an issue where `AsyncPregelLoop` could leave behind an
orphaned `stream.wait()` task, resulting in warnings like:

```
Task was destroyed but it is pending!
```

### Related Discussion
This PR is in response to:
[langchain-ai/langgraph#6163](https://github.com/langchain-ai/langgraph/discussions/6163)


### Problem

* In the async path, `get_waiter()` was creating a new `asyncio.Task`
via

  ```python
  aioloop.create_task(stream.wait())
  ```

  but never tracked or cleaned it up.
* On cancellation or shutdown, these tasks remained pending and produced
warnings.

### Solution

* Changed `get_waiter()` to:

  * Maintain a **single waiter task** (similar to the sync path).
  * Auto-clear the reference when the task finishes.
* Added `_cleanup_waiter()`:

* On exit, attempt to wake the waiter (`stream._count.release()` if
available).
* Otherwise, cancel and `await` the pending task to ensure proper
cleanup.
* Wrapped the `while loop.tick():` block in a `try/finally` to guarantee
`_cleanup_waiter()` runs on exit.
* Added missing `import contextlib`.

### Impact

* Prevents orphaned `stream.wait()` tasks.
* Removes noisy `"Task was destroyed but it is pending!"` warnings.
* Behavior of async streaming remains unchanged, only lifecycle
management improved.

### Test Plan

* Reproduced the issue by running async streaming with cancellation.
* Verified warnings no longer appear after the fix.
* Ran existing test suite (all passing).

### Notes

* Sync and Async implementations now follow the same principle: *only
one waiter at a time, always cleaned up on exit*.
* Backwards-compatible; no API changes.


### Repro & Verification

To confirm the issue and the fix I used the following minimal repro
snippet:

```python
# lg_repro.py
import asyncio
import os

# Enable asyncio debug logs to surface pending task warnings
os.environ.setdefault("PYTHONASYNCIODEBUG", "1")

from langgraph.graph import START, END, StateGraph

State = dict

# Slow async node: processes once, then sleeps to keep the waiter alive
async def slow_node(state: State) -> State:
    await asyncio.sleep(0.2)  # simulate work
    state["count"] = state.get("count", 0) + 1
    await asyncio.sleep(1.0)  # keep stream.wait() waiter active
    return state

# Build simple graph: START -> slow_node -> END
builder = StateGraph(State)
builder.add_node("slow", slow_node)
builder.add_edge(START, "slow")
builder.add_edge("slow", END)
graph = builder.compile()

async def run_and_cancel():
    # astream with messages mode triggers internal stream.wait() waiter
    async def consumer():
        async for _ in graph.astream({"msg": "hi"}, stream_mode="messages"):
            await asyncio.sleep(0.05)

    t = asyncio.create_task(consumer(), name="astream-consumer")

    # Allow the stream to start, then cancel the consumer
    await asyncio.sleep(0.1)
    t.cancel()
    try:
        await t
    except asyncio.CancelledError:
        pass

    # Let loop settle to show pending waiter task if not cleaned
    await asyncio.sleep(0.05)

def main():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.set_debug(True)
    try:
        loop.run_until_complete(run_and_cancel())
    finally:
        # If the internal waiter is not cleaned, closing the loop will warn
        loop.close()

if __name__ == "__main__":
    main()
````

**How to run**

```powershell
# Before (main branch)
git checkout main
pip install -e libs/langgraph
$env:PYTHONASYNCIODEBUG=1; python lg_repro.py

# After (patched branch)
git checkout async-waiter-cleanup
pip install -e libs/langgraph
$env:PYTHONASYNCIODEBUG=1; python lg_repro.py
```

**Observed results**

* **main branch (before fix):**
  Shows warnings like:

  ```
  Task was destroyed but it is pending!
  ... coro=<AsyncQueue.wait() ...>
  created at langgraph/pregel/main.py:2927
  ```
* **patched branch (after fix):**
No warnings. The single waiter is properly cleaned up on exit via
`_cleanup_waiter()` (release semaphore if available, then cancel/await).

---

This confirms that the patch removes the orphaned `stream.wait()` task
and prevents
`"Task was destroyed but it is pending!"` warnings during
cancellation/shutdown.

---------

Co-authored-by: Caspar Broekhuizen <caspar@langchain.dev>
2025-09-23 17:11:17 -07:00
2025-06-10 10:06:08 -07:00
2025-09-09 14:22:19 +00:00
2024-03-15 14:31:59 -07:00

LangGraph Logo

Version Downloads Open Issues Docs

Trusted by companies shaping the future of agents including Klarna, Replit, Elastic, and more LangGraph is a low-level orchestration framework for building, managing, and deploying long-running, stateful agents.

Get started

Install LangGraph:

pip install -U langgraph

Then, create an agent using prebuilt components:

# pip install -qU "langchain[anthropic]" to call the model

from langgraph.prebuilt import create_react_agent

def get_weather(city: str) -> str:
    """Get weather for a given city."""
    return f"It's always sunny in {city}!"

agent = create_react_agent(
    model="anthropic:claude-3-7-sonnet-latest",
    tools=[get_weather],
    prompt="You are a helpful assistant"
)

# Run the agent
agent.invoke(
    {"messages": [{"role": "user", "content": "what is the weather in sf"}]}
)

For more information, see the Quickstart. Or, to learn how to build an agent workflow with a customizable architecture, long-term memory, and other complex task handling, see the LangGraph basics tutorials.

Core benefits

LangGraph provides low-level supporting infrastructure for any long-running, stateful workflow or agent. LangGraph does not abstract prompts or architecture, and provides the following central benefits:

  • Durable execution: Build agents that persist through failures and can run for extended periods, automatically resuming from exactly where they left off.
  • Human-in-the-loop: Seamlessly incorporate human oversight by inspecting and modifying agent state at any point during execution.
  • Comprehensive memory: Create truly stateful agents with both short-term working memory for ongoing reasoning and long-term persistent memory across sessions.
  • Debugging with LangSmith: Gain deep visibility into complex agent behavior with visualization tools that trace execution paths, capture state transitions, and provide detailed runtime metrics.
  • Production-ready deployment: Deploy sophisticated agent systems confidently with scalable infrastructure designed to handle the unique challenges of stateful, long-running workflows.

LangGraphs ecosystem

While LangGraph can be used standalone, it also integrates seamlessly with any LangChain product, giving developers a full suite of tools for building agents. To improve your LLM application development, pair LangGraph with:

  • LangSmith — Helpful for agent evals and observability. Debug poor-performing LLM app runs, evaluate agent trajectories, gain visibility in production, and improve performance over time.
  • LangGraph Platform — Deploy and scale agents effortlessly with a purpose-built deployment platform for long running, stateful workflows. Discover, reuse, configure, and share agents across teams — and iterate quickly with visual prototyping in LangGraph Studio.
  • LangChain Provides integrations and composable components to streamline LLM application development.

Note

Looking for the JS version of LangGraph? See the JS repo and the JS docs.

Additional resources

  • Guides: Quick, actionable code snippets for topics such as streaming, adding memory & persistence, and design patterns (e.g. branching, subgraphs, etc.).
  • Reference: Detailed reference on core classes, methods, how to use the graph and checkpointing APIs, and higher-level prebuilt components.
  • Examples: Guided examples on getting started with LangGraph.
  • LangChain Forum: Connect with the community and share all of your technical questions, ideas, and feedback.
  • LangChain Academy: Learn the basics of LangGraph in our free, structured course.
  • Templates: Pre-built reference apps for common agentic workflows (e.g. ReAct agent, memory, retrieval etc.) that can be cloned and adapted.
  • Case studies: Hear how industry leaders use LangGraph to ship AI applications at scale.

Acknowledgements

LangGraph is inspired by Pregel and Apache Beam. The public interface draws inspiration from NetworkX. LangGraph is built by LangChain Inc, the creators of LangChain, but can be used without LangChain.

Languages
Python 99.6%
Makefile 0.2%
TypeScript 0.1%