Description
The health-monitor's stale-socket detection for the Discord provider triggers a full gateway restart instead of reconnecting only the Discord WebSocket. This causes significant downtime for users with active Discord integrations.
Impact
- 22+ full gateway restarts per day on our setup
- 10-15 seconds of total downtime per restart (crons interrupted, Discord offline, canvas unmounted)
- Cumulative: ~5 minutes of avoidable downtime per day
- 445 stale-socket restarts observed in one log rotation window
Evidence
# Consistent 35-65 minute cycle
2026-03-27T22:19:58 [health-monitor] started (interval: 300s)
2026-03-27T23:24:58 [health-monitor] [discord:default] restarting (reason: stale-socket)
2026-03-28T00:25:04 [health-monitor] [discord:default] restarting (reason: stale-socket)
2026-03-28T00:31:24 [discord] gateway: WebSocket connection closed with code 1006
2026-03-28T01:26:06 [discord] gateway: WebSocket connection closed with code 1005
2026-03-28T02:00:12 [health-monitor] [discord:default] restarting (reason: stale-socket)
Close codes: 1006 (abnormal — no close frame), 1005 (library timeout)
Root Cause Hypothesis
Discord heartbeat starvation during heavy cron execution. With 40+ cron jobs (some spawning Claude CLI subprocesses), the Node.js event loop is contended. When blocked for >41.25s (Discord's heartbeat interval), Discord drops the WebSocket. The health-monitor detects the stale socket and restarts the entire gateway.
Suggested Improvements
-
Surgical Discord reconnect (P1): When stale-socket is Discord-specific, call discord.provider.reconnect() instead of gateway.restart(). This preserves the gateway, cron scheduler, canvas host, and all other services.
-
Configurable stale threshold (P2): Allow setting how many consecutive stale checks before triggering a restart. Currently restarts on first detection. Threshold of 2-3 would allow transient drops to self-heal via Discord's built-in resume.
-
Discord heartbeat jitter config (P3): Expose discord.heartbeatJitterMs to send heartbeats slightly early, providing headroom during event loop contention.
Environment
- OpenClaw: 2026.3.24
- macOS Darwin 25.3.0, Mac Mini M4 Pro
- Node.js 24 LTS
- 40+ cron jobs, 17 agents
- 1 Discord server, 2 channels, Message Content Intent enabled
Workaround
Deployed a launchd watchdog that monitors gateway health every 15 minutes and auto-restarts crashed services. Mitigates downtime but doesn't fix root cause.
Description
The health-monitor's
stale-socketdetection for the Discord provider triggers a full gateway restart instead of reconnecting only the Discord WebSocket. This causes significant downtime for users with active Discord integrations.Impact
Evidence
Close codes: 1006 (abnormal — no close frame), 1005 (library timeout)
Root Cause Hypothesis
Discord heartbeat starvation during heavy cron execution. With 40+ cron jobs (some spawning Claude CLI subprocesses), the Node.js event loop is contended. When blocked for >41.25s (Discord's heartbeat interval), Discord drops the WebSocket. The health-monitor detects the stale socket and restarts the entire gateway.
Suggested Improvements
Surgical Discord reconnect (P1): When stale-socket is Discord-specific, call
discord.provider.reconnect()instead ofgateway.restart(). This preserves the gateway, cron scheduler, canvas host, and all other services.Configurable stale threshold (P2): Allow setting how many consecutive stale checks before triggering a restart. Currently restarts on first detection. Threshold of 2-3 would allow transient drops to self-heal via Discord's built-in resume.
Discord heartbeat jitter config (P3): Expose
discord.heartbeatJitterMsto send heartbeats slightly early, providing headroom during event loop contention.Environment
Workaround
Deployed a launchd watchdog that monitors gateway health every 15 minutes and auto-restarts crashed services. Mitigates downtime but doesn't fix root cause.