LibreChat/api/server/services/Endpoints/agents
Danny Avila 2d794a0439
💓 fix: Detect Silently Dead Redis Sockets With Deadline-Bounded Heartbeats (#15539)
* fix: Detect silently dead Redis sockets with deadline-bounded heartbeats

A Redis peer can vanish without a FIN or RST (a dropped NAT entry, a proxy
failover, a migrated VM). The socket stays `ready` from ioredis's point of
view, no error fires, and every command on it waits for the kernel's
retransmission timeout — about fifteen minutes at Linux defaults.

Observed in production: while the main client's socket was dead,
POST /api/agents hung until Cloudflare returned a 524; while the subscriber's
socket was dead, every attach failed with "Timed out synchronizing Redis
subscription" for sixteen minutes across five conversations.

`REDIS_PING_INTERVAL` could not help — it awaits ping() with no deadline, so
the probe just joins everything else that is waiting.

- Add startRedisHeartbeat: races a PING against a deadline and destroys the
  socket when the probe goes unanswered, so ioredis reconnects, replays queued
  commands, and re-subscribes a subscriber's channels.
- Destroy the stream rather than disconnect(true), whose FIN a vanished peer
  never acknowledges; cluster clients fall back to the regular reconnect.
- Add createIoRedisSubscriber so every dedicated subscriber gets an error
  listener and its own heartbeat, and route the generation-stream and both
  subagent-routing subscribers through it.
- Set ioredis keepAlive from REDIS_KEEP_ALIVE (default 10s).
- Add REDIS_PING_TIMEOUT, REDIS_SUBSCRIBER_PING_INTERVAL and REDIS_KEEP_ALIVE,
  documented in .env.example.
- Give the fake RESP server a silent mode and a SUBSCRIBE reply so the
  dead-peer case can be tested against real clients.

* fix: Probe each cluster node and refuse a non-positive heartbeat deadline

A cluster-level PING is routed to one node, so a healthy reply proved nothing
about the other sockets. The heartbeat now resolves its targets each tick —
every node for a cluster, the client itself otherwise — and races an
independent deadline per target, tearing down only the socket that stops
answering. A non-positive deadline disables the heartbeat with a warning
instead of forcing a reconnect on every tick, and the single-probe test no
longer depends on wall-clock slack.
2026-09-03 09:45:16 -04:00
..
addedConvo.js 🚧 fix: Narrow Chat MCP Selections to What the Picker May Offer (#15478) 2026-09-01 18:47:36 -04:00
addedConvo.spec.js 🛟 fix: Stop Agents When Code Resources Cannot Recover (#14651) 2026-08-07 00:33:28 -04:00
backgroundCompletion.js 🛎️ feat: Wake Agents on Background Tool Completion (#15350) 2026-08-30 08:56:41 -04:00
build.js 🚧 fix: Narrow Chat MCP Selections to What the Picker May Offer (#15478) 2026-09-01 18:47:36 -04:00
eventChildLease.js 📨 feat: Continue Bound Child Agents from Events (#15112) 2026-08-23 01:15:57 -04:00
index.js
initialize.js 🧊 feat: Persist Context Fading Tier Across Agent Runs (#15505) 2026-09-02 20:29:43 -04:00
initialize.spec.js 🕹️ feat: Configure BYOM Tool Permissions (#15504) 2026-09-02 10:38:00 -04:00
skillDeps.js 🪶 feat: Enable Standalone Skill Authoring (#15480) 2026-09-01 18:39:05 -04:00
skillDeps.spec.js
subagentThreadStore.js 💓 fix: Detect Silently Dead Redis Sockets With Deadline-Bounded Heartbeats (#15539) 2026-09-03 09:45:16 -04:00
subagentThreadStore.spec.js 💓 fix: Detect Silently Dead Redis Sockets With Deadline-Bounded Heartbeats (#15539) 2026-09-03 09:45:16 -04:00
title.js 🛂 feat: Filter Model-Bound Content by Source (#14425) 2026-08-21 22:43:32 -04:00
title.test.js 🛂 feat: Filter Model-Bound Content by Source (#14425) 2026-08-21 22:43:32 -04:00