#150955·OpenClaw

Gateway RSS growth freezes the event loop: process accepts TCP but cannot complete a websocket handshake

Author: meircohenCreated Sep 17, 2026Updated Sep 17, 2026

Environment

  • openclaw 2026.9.2 (3928bad)
  • Node 22.23.2, Debian 12
  • Long-running openclaw gateway run under a supervisor, Telegram + WhatsApp channels, several concurrent agent sessions

Summary

Gateway RSS grows under load until major GC pauses freeze the event loop for minutes at a time. While frozen the process still holds its listening socket, so it accepts TCP but cannot complete a websocket handshake. From outside it looks like the gateway is up; in practice nothing is dispatched.

A scheduled restart clears it and the cycle repeats.

Evidence

Peak RSS against event-loop stalls on one host, same day:

Time RSS Longest stall in that window
03:03 3,178 MB 164,367 ms
04:07 2,173 MB 78,373 ms
06:33 1,592 MB 34,541 ms

Stalls are reported by the gateway's own diagnostic:

liveness heartbeat delayed: overdue=134297ms elapsed=164367ms; deferring recovery decisions

and clients see:

[openclaw] Could not start the CLI.
[openclaw] Reason: Opening handshake has timed out

The openclaw --version path still answers instantly during these windows, so the process is alive; only work that needs the event loop is blocked.

Growth rate

Growth tracks failure volume rather than uptime. A quiet 90-minute window held flat at about 1.6 GB, while a window with a dispatch failure storm took one process from 619 MB to 4,670 MB in 54 minutes before it died.

What I ruled out

  • Database contention. Hot queries on the state SQLite run in 2–4 ms in isolation, plans use their indexes, fragmentation is 0.5%, and the WAL is stable. The slow-transaction rate is flat across a whole day and does not correlate with the stalls.
  • A specific hung tool call. The stalls occur with no child processes running.
  • Disk pressure. Present on the host at one point, but the stalls continued unchanged after it was resolved with 200 GB free.

Why it is awkward operationally

The obvious mitigation is a memory-triggered restart, but the restart path itself needs the gateway to answer a health query in order to confirm it is idle. A gateway in this state cannot answer, so the safety interlock refuses and the only remedy is blocked exactly when it is needed. That is a separate design point, but it is why this failure tends to require manual intervention.

What would help

Some way to bound or observe the growth would make this manageable even without a root-cause fix: a heap breakdown exposed on the diagnostic endpoint, or an explicit cap on whatever accumulates per session, so operators can see what is retained rather than inferring it from RSS.

Happy to capture a heap snapshot from a live instance if that would be useful, or to run a build with extra instrumentation.