Skip to content

Relay WebSocket drops every 2-14 minutes with code 1006 -- no keepalive/ping on daemon connection #1340

Description

@lkmotto

Description

The daemon's WebSocket connection to relay.factory.ai drops every few minutes with code 1006 ("Connection ended" -- abnormal closure, no WebSocket close frame sent). The daemon reconnects within ~1 second, but during that reconnect window, web sessions become "session unavailable" because the relay's orphan grace period is shorter than the reconnect time.

Root Cause

The daemon does not send WebSocket-level ping/pong frames and does not enable TCP keepalive (socket.setKeepAlive()) on the relay WebSocket connection. Intermediate infrastructure (home router NAT, ISP gateways, load balancers) drops the idle TCP connection after their timeout window (typically 60-300 seconds).

This matches the classic WebSocket timeout pattern described at websocket.org/guides/troubleshooting/timeout: connections die silently without FIN/RST when intermediate devices drop idle NAT mappings.

Evidence (from ~/.factory/logs/droid-log-single.log)

Drops consistently reoccur, with intervals of ~2-14 minutes between reconnects:

[2026-07-28T00:01:38.605Z] WARN: [Relay] Disconnected | Context: {"code":1006,"reason":"Connection ended"}
[2026-07-28T00:01:39.618Z] INFO: [Relay] Connected

[2026-07-28T00:08:33.976Z] WARN: [Relay] Disconnected | Context: {"code":1006,"reason":"Connection ended"}
[2026-07-28T00:08:34.942Z] INFO: [Relay] Connected

[2026-07-28T00:22:25.158Z] WARN: [Relay] Disconnected | Context: {"code":1006,"reason":"Connection ended"}
[2026-07-28T00:22:26.231Z] INFO: [Relay] Connected

The reconnection is fast (~1 second), but the relay's orphan grace window appears to be ~2 seconds, which is too tight for this reconnect frequency.

Environment

  • OS: Windows 10 (build 26200), BYOM
  • Factory: latest daemon (auto-updating)
  • Network: behind consumer NAT (192.168.1.x), Tailscale installed
  • Windows TCP keepalive registry settings were tuned (KeepAliveTime=30000ms) but had no effect -- Node.js must explicitly call setKeepAlive() for these to take effect

Proposed Fix

In the daemon's relay WebSocket connection code, add one or both of:

  1. TCP keepalive (minimal, catches dead connections after ~30s):

    socket.setKeepAlive(true, 30000); // enable keepalive, 30s idle before first probe
  2. WebSocket-level ping/pong (proper fix, keeps intermediate proxies alive):

    setInterval(() => ws.ping(), 25000); // 25s beats all common proxy defaults

Both are one-liners. The net-keepalive npm package provides portable TCP_KEEPIDLE/TCP_KEEPINTVL for finer control if needed.

Impact

Every web-initiated session on this BYOM computer breaks after the first message exchange. Local CLI sessions work fine because they go through the daemon's local transport, not the relay WebSocket.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions