Summary
NanoClaw crashes with a JavaScript heap OOM error after running for ~40 hours.
launchd restarts the process silently, so the bot stops responding briefly
then recovers — easy to miss without checking logs/nanoclaw.error.log.
Error
FATAL ERROR: Ineffective mark-compacts near heap limit
Allocation failed - JavaScript heap out of memory
[72182:0xaa880c000] 147088567 ms: Scavenge 4057.3 (4104.4) -> 4052.5 MB
Root cause
connectInternal() in src/channels/whatsapp.ts creates a new makeWASocket
instance on every reconnection without destroying the previous socket. The old
socket's event listeners (connection.update, creds.update, messages.upsert),
makeCacheableSignalKeyStore LRU cache, and internal Baileys buffers remain alive
via closure references.
WhatsApp reconnections are frequent (network blips, phone sleep, WA server drops),
so ghost sockets accumulate over time until the heap is exhausted.
Secondary risk: the old socket can still fire connection.update after this.sock
is reassigned, causing the old listener to call connectInternal() again —
potentially creating a cascade of reconnects.
Fix
Remove event listeners and call end(undefined) on the old socket at the top of
connectInternal() before creating the new one. Happy to submit a PR.
Summary
NanoClaw crashes with a JavaScript heap OOM error after running for ~40 hours.
launchdrestarts the process silently, so the bot stops responding brieflythen recovers — easy to miss without checking
logs/nanoclaw.error.log.Error
Root cause
connectInternal()insrc/channels/whatsapp.tscreates a newmakeWASocketinstance on every reconnection without destroying the previous socket. The old
socket's event listeners (
connection.update,creds.update,messages.upsert),makeCacheableSignalKeyStoreLRU cache, and internal Baileys buffers remain alivevia closure references.
WhatsApp reconnections are frequent (network blips, phone sleep, WA server drops),
so ghost sockets accumulate over time until the heap is exhausted.
Secondary risk: the old socket can still fire
connection.updateafterthis.sockis reassigned, causing the old listener to call
connectInternal()again —potentially creating a cascade of reconnects.
Fix
Remove event listeners and call
end(undefined)on the old socket at the top ofconnectInternal()before creating the new one. Happy to submit a PR.