Hi LangChain team,
Background — what started this
Participants interacting with our AI moderator reported long pauses, then no responses ~25–30 minutes into interviews — sometimes unable to send at all.
We investigated and found the moderator-langgraph API pods repeatedly OOMKilled / restarting with exit 137: memory grows with traffic until a pod hits its limit, restarts, and climbs again. Raising the limit (2Gi→8Gi) only widened the gap between restarts — the growth continued. It scales with interview/run volume and began with our cutover to @langchain/react useStream, pointing at retained per-stream server state rather than our own app objects.
The reproduction below isolates that accumulation on a single pod.
We are investigating what looks like retained stream subscriptions / goroutines in a LangGraph API deployment under browser-driven agentic conversation traffic and client disconnects.
Setup
Frontend:
@langchain/react@^1.0.26@langchain/langgraph-sdk@^1.9.25@langchain/core@^1.1.48- React
18.3.1 - React Router
7.12.0 - Vite
5.4.11 - Node
23.6.0 - Bun
1.3.0
Backend:
- Python
3.13.14 langgraph-api==0.10.3langgraph==1.2.4langgraph-sdk==0.4.2langchain-core==1.4.0langchain-openai==1.2.2langchain-anthropic==1.4.0
Deployment/runtime:
- Kubernetes deployment
- LangGraph API Docker image for
langgraph-api==0.10.3 - Postgres-backed LangGraph runtime/checkpointing, with Redis used for streaming/pubsub/queueing
- Go core API process is present and exposes pprof internally
Frontend Architecture
The browser uses the standard @langchain/react v1 hook:
const thread = useStream<ModeratorValues>({
apiUrl,
assistantId,
threadId,
});
We proxy LangGraph routes through our UI server, forwarding these SDK requests:
GET /threads/:threadId/statePOST /threads/:threadId/commandsPOST /threads/:threadId/stream/events
We are not using a custom transport, custom fetch, HttpAgentServerAdapter, polling, retries, recovery logic, or proxy-side duplicate stream cancellation.
The proxy forwards browser aborts to upstream LangGraph fetches via request.signal, so closed browser contexts / navigation aborts should propagate to upstream SSE requests.
On the frontend lifecycle side, we follow the join/rejoin guidance: disconnect without cancelling the run on page lifecycle teardown, then remount/rejoin with the same threadId where applicable.
The reproduction below mainly exercises fresh browser contexts and browser-context close, not manual refresh/rejoin of the same thread.
What Works
For the affected runs, command dispatch and graph execution look healthy:
/commandsrequests return200- Runs are created once
- Graph executions complete successfully
- AI responses are non-empty
- Threads return to
idle
So this does not currently look like duplicate backend runs or failed generation.
Tagged Reproduction Artifacts
The files mentioned below are available in this Drive folder:
All attached files in that folder are from the adhoc_0.10.3 investigation:
adhoc_0.10.3_browser_2.csv- Generated by a small browser automation script for this reproduction.
- This script opens fresh browser contexts, runs agentic conversations through the real React app and
@langchain/react, then closes the browser context. - It does not simulate refresh/rejoin of the same thread.
go-pprof-adhoc_0.10.3_2.csv- Generated by repeatedly sampling the Go pprof endpoint inside the LangGraph API pod.
- The sampler runs inside the pod against
127.0.0.1:50060/debug/pprof/goroutine?debug=2and records memory, total goroutines,threads_server,redis_streaming,go_redis_pubsub,grpc_stream_interceptor, oldest parkedselect, and top goroutine states.
heap-adhoc_0.10.3_2.csv- Generated from sampled Go heap / runtime memory metrics for the same ad hoc run.
- It tracks live heap bytes, heap in-use, Go total memory, heap stacks, goroutine count, and container working set.
go-pprof-adhoc_0.10.3_baseline_after_restart.csv- Clean baseline sample immediately after restarting the API pod showed approximately:
- memory:
371Mi - total goroutines:
41 threads_server=0redis_streaming=0go_redis_pubsub=0
What Looks Wrong
With an automation script talking to our agent through the real React app and @langchain/react, the Go core API process appears to accumulate stream-related goroutines.
The browser automation was running against one LangGraph API pod. Before that pod restarted, the browser-side CSV showed:
elapsed time: ~26.4 minutes
turns sent: 493
completed conversations: 34
errors: 4
container memory: 426Mi -> 760Mi
container restarts: 0 -> 1 shortly after
The matching Go pprof samples for the same pre-restart process window showed:
sample window: 22:49:46 -> 23:12:55 UTC
memory: 491Mi -> 760Mi
total_goroutines: 124 -> 403
total_goroutines peak: 428
threads_server: 16 -> 90
redis_streaming: 16 -> 90
go_redis_pubsub: 40 -> 180
go_redis_pubsub peak: 190
grpc_stream_interceptor: 20 -> 90
grpc_stream_interceptor peak: 95
oldest_select_min: 7 -> 30
The sampled pprof data points to retained LangGraph Threads.Stream gRPC handlers and Redis PubSub relay goroutines, not our Python graph code.
At 23:13:19, the browser CSV reported restarts=1, and the next pprof sample had stream-related counts reset to zero:
23:12:55 before restart:
memory: 760Mi
total_goroutines: 403
threads_server: 90
redis_streaming: 90
go_redis_pubsub: 180
23:13:56 after restart:
memory: 333Mi
total_goroutines: 45
threads_server: 0
redis_streaming: 0
go_redis_pubsub: 0
The concern is not just a transient spike during traffic: once these stream-related goroutine and memory counts accumulate, they do not appear to return to the clean baseline after traffic stops; they reset only when the pod restarts.
We have previously noticed that when this memory growth continues, our server pods eventually restart with OOM symptoms. This run restarted before the end of the automation window, which is consistent with that failure mode.
Question
Is this expected behavior for resumable stream subscriptions in langgraph-api==0.10.3, or could this indicate a stream cleanup issue in the Go core API / Redis PubSub relay layer?
Specifically:
- Should client disconnects, closed browser contexts, or browser navigation aborts of
/stream/eventsreliably tear down the corresponding GoThreads.Streamhandler and Redis PubSub subscription? - Is
stream.disconnect()from@langchain/reactexpected to be sufficient client-side cleanup without cancelling the server run? - Are there known issues in
langgraph-api==0.10.3around retained stream subscriptions, Redis PubSub goroutines, or resumable stream cleanup? - Is there a recommended server-side timeout/TTL/configuration for abandoned
/stream/eventssubscriptions? - Would you recommend a different frontend join/rejoin pattern for React apps that proxy the LangGraph API through their own server?
Happy to provide any additional information to any questions you may have around architecture, setup, or debug information.
Any help is greatly appreciated, thanks in advance.