Bug Description
Token consolidation (maybe_consolidate_by_tokens) can enter a failure loop that blocks the WeChat long-poll channel and eventually causes macOS launchd to kill the nanobot process.
Steps to Reproduce
- Have an extended conversation via WeChat (64+ messages, ~61k+ tokens)
- Token count reaches
context_window_tokens threshold
pick_consolidation_boundary fails to find enough user-turn boundaries to reduce tokens below budget × consolidation_ratio target
- After 5 rounds (
_MAX_CONSOLIDATION_ROUNDS = 5), consolidation exits with "no safe boundary" log message
- Next incoming message triggers consolidation again → infinite failure loop
- WeChat long-poll is blocked because consolidation runs synchronously under an
asyncio.Lock
- Process appears "idle" to macOS launchd → SIGTERM sent
Root Causes
1. Consolidation failure is non-fatal but loops forever
When pick_consolidation_boundary returns None, the function logs "no safe boundary" and exits. The next message to arrive re-triggers the same logic, creating an infinite retry loop under the session lock.
2. No timeout on consolidation lock
maybe_consolidate_by_tokens holds an asyncio.Lock for the entire session with no timeout. All subsequent messages for that session queue behind the lock, effectively blocking the channel.
3. macOS "inefficient" process kill
launchd considers a process "inefficient" if it has no external network activity for a period. During the consolidation retry loop, the process is CPU-bound but not network-active, triggering SIGTERM.
Logs
2026-05-11 10:00:54 Consolidation round 0: 61460/65536 tokens, chunk=64 msgs
2026-05-11 10:01:07 Consolidation: no safe boundary (round 1)
[WeChat long-poll completely blocked from this point]
2026-05-11 10:17:52 launchd[1]: [ai.nanobot.agent [44514]:] signaled service: Terminated: 15
Suggested Fixes
- Add timeout to consolidation: If consolidation cannot complete within N seconds, abandon it and let messages through rather than holding the lock.
- Make consolidation failure non-blocking: After
_MAX_CONSOLIDATION_ROUNDS, log a warning but don't re-trigger on every message. Add a back-off (e.g., only retry after N new messages or N minutes).
- Consider partial consolidation: If full consolidation fails, reduce tokens by whatever boundary was found rather than giving up entirely.
- Add monitoring: Log when consolidation fails so this condition is visible before the process is killed.
Environment
- nanobot v0.1.5.post3
- macOS (launchd)
- WeChat channel
- Model: qwen/qwen3-next-80b-a3b-instruct (free tier, 65k context)
Bug Description
Token consolidation (
maybe_consolidate_by_tokens) can enter a failure loop that blocks the WeChat long-poll channel and eventually causes macOS launchd to kill the nanobot process.Steps to Reproduce
context_window_tokensthresholdpick_consolidation_boundaryfails to find enough user-turn boundaries to reduce tokens belowbudget × consolidation_ratiotarget_MAX_CONSOLIDATION_ROUNDS = 5), consolidation exits with "no safe boundary" log messageasyncio.LockRoot Causes
1. Consolidation failure is non-fatal but loops forever
When
pick_consolidation_boundaryreturnsNone, the function logs "no safe boundary" and exits. The next message to arrive re-triggers the same logic, creating an infinite retry loop under the session lock.2. No timeout on consolidation lock
maybe_consolidate_by_tokensholds anasyncio.Lockfor the entire session with no timeout. All subsequent messages for that session queue behind the lock, effectively blocking the channel.3. macOS "inefficient" process kill
launchd considers a process "inefficient" if it has no external network activity for a period. During the consolidation retry loop, the process is CPU-bound but not network-active, triggering SIGTERM.
Logs
Suggested Fixes
_MAX_CONSOLIDATION_ROUNDS, log a warning but don't re-trigger on every message. Add a back-off (e.g., only retry after N new messages or N minutes).Environment