Skip to content

[BUG] Infinite render loop when contact has no presence data — floods WAHA with requests and hangs TUI #88

Description

@iamfinky

Describe the bug

When opening a 1-on-1 chat where the contact has no current presence data, the TUI enters an infinite render loop that floods the WAHA server with hundreds of presence GET +
subscribe requests per second. This starves the UI thread and causes the TUI to hang and become unresponsive within seconds.

The root cause is in the chat header render function: loadChatDetails(chatId) is called as a fallback whenever presence?.presences is empty. Because loadChatDetails updates
app state on completion, it triggers a re-render — and if the presence response still has an empty presences array (which NOWEB returns until a subscription event is pushed), the
condition remains false and loadChatDetails is called again immediately, looping forever.

To Reproduce

  1. Run waha-tui with --debug option as waha-tui --debug.
  2. Open any 1-on-1 chat with a contact who is currently offline / not actively present.
  3. The TUI will hang within a few seconds. In the WAHA server logs you will see ~20+ pairs of GET /api/{session}/presence/{chatId} + POST /api/{session}/presence/{chatId}/subscribe firing every second.

Expected behavior

Presence should be fetched once when the chat is opened (or on a timed interval), not on every render cycle. The TUI should remain responsive even when a contact's presence data is
temporarily empty.

Environment

  • WAHA-TUI Version: 1.6.4
  • Bun Version: 1.3.14
  • OS: Ubuntu 24.04.4 LTS
  • Terminal Emulator: iTerm2
  • WAHA Engine: NOWEB

WAHA Configuration

  • I am using the latest version of WAHA-TUI.
  • I have a running WAHA server accessible at the configured URL.
  • I have enabled tagsEventsOn if using WEBJS engine (if applicable).

Debug Logs

Click to expand debug logs
# WAHA server logs — presence flood visible within seconds of opening an offline contact's chat:
GET /api/default/presence/[REDACTED]@s.whatsapp.net → 200 (1ms)
POST /api/default/presence/[REDACTED]@s.whatsapp.net/subscribe → 201 (1ms)
GET /api/default/presence/[REDACTED]@s.whatsapp.net → 200 (1ms)
POST /api/default/presence/[REDACTED]@s.whatsapp.net/subscribe → 201 (1ms)
# ... repeats ~20 times per second until TUI is killed

Additional context

The problematic call is in dist/index.js around the chat header render function (v1.6.4 ~line 28513):

} else {
headerSubtitle = "";
loadChatDetails(state.currentChatId); // called on every render when presences is empty
}

loadChatDetails calls presenceControllerGetPresence then presenceControllerSubscribe, both of which update app state and trigger a re-render. Since NOWEB returns { presences: [] }
until a subscription event is pushed by the server, the empty-presences condition is always true on the next render, creating a tight loop.

Workaround: Adding a 30-second per-chatId cooldown guard at the top of loadChatDetails in the cached source breaks the loop:

const _loadChatDetailsTs = new Map();
async function loadChatDetails(chatId) {
const now = Date.now();
if (_loadChatDetailsTs.has(chatId) && now - _loadChatDetailsTs.get(chatId) < 30000) return;
_loadChatDetailsTs.set(chatId, now);
// ... rest of function unchanged

The proper fix would be to move loadChatDetails out of the render/compute path and into a lifecycle effect that fires when currentChatId changes.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions