Skip to content

Fix TTS streamer hang that freezes crab stop indefinitely#2

Open
TheDiscordian wants to merge 1 commit into
mainfrom
fix-tts-streamer-hang
Open

Fix TTS streamer hang that freezes crab stop indefinitely#2
TheDiscordian wants to merge 1 commit into
mainfrom
fix-tts-streamer-hang

Conversation

@TheDiscordian

Copy link
Copy Markdown
Owner

Problem

Holding SUPER+A occasionally leaves Beatrice/Crab stuck — the spoken response never comes and the interaction silently dies. Diagnosed from a live hung process: a crab stop blocked in do_wait for 5+ minutes on a tts-streamer child whose read cursor was 2407 bytes past EOF of a truncated log.

Root cause

lib/tts-streamer tails the stream-json debug log and its only non-signal exit is reading a {"type":"result"} event. Two paths make it loop forever, with the parent crab stop blocked on wait "$_TTS_STREAMER_PID":

  1. No result event — if claude crashed, was killed, or got rate-limited mid-stream, it may never emit a result line.
  2. Stranded cursor — a streamer still running from an overlapping request gets its read cursor stranded past EOF when the next session truncates the shared DEBUGLOG (: > "$DEBUGLOG"), so it never reaches the result event.

This is independent of the recent sliding-window summarization PR — that only touched common.sh conversation handling, not the start/stop or streamer path.

Fix

  • Append a synthetic {"type":"result"} terminator after the claude call so the streamer always gets a stop signal. extract-response ignores a result line with no result field, so it's harmless on the success path.
  • Kill any prior streamer before truncating the shared log, so an overlapping streamer can't be stranded past EOF.
  • Exit the streamer if its launching crab process dies (ppid changes) — covers the orphaned-parent case, including under a systemd user subreaper where orphans don't reparent to PID 1.

Testing

  • bash -n / py_compile clean.
  • Result-less stream + terminator → streamer exits (no hang). ✅
  • Orphaned streamer (parent killed) → exits on ppid change. ✅
  • pkill -f overlap pattern kills a prior streamer before truncation. ✅
  • Normal stream (text blocks + real result) → still exits promptly, extract-response returns the real result. ✅

The TTS streamer tails the stream-json debug log and only exits when it
reads a {"type":"result"} event. Two paths left it looping forever, with
the parent crab stop blocked on `wait` for it:

1. If claude crashed, was killed, or got rate-limited mid-stream it may
   never emit a result event.
2. A streamer still running from an overlapping request gets its read
   cursor stranded past EOF when the next session truncates the shared
   log, so it never reaches the result event.

Fixes:
- Append a synthetic {"type":"result"} terminator after the claude call
  so the streamer always gets a stop signal (extract-response ignores a
  result line with no "result" field, so it's harmless on success).
- Kill any prior streamer before truncating the shared log, so an
  overlapping streamer can't be stranded.
- Exit the streamer if its launching crab process dies (ppid changes),
  covering the orphaned-parent case under a systemd user subreaper.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant