Live transcription stalls after ~40 min: whisper-server deadlocks because its stderr pipe is never drained
Nobody has claimed this yet.
- Dominant language
- No language data
- Stars
- 1.1k
- Forks
- 108
- PR merge metrics
- No merged PRs in 30d
Description
Version: Memo 1.8.7 (bundle com.memo.whisper), bundled whisper-server 1.8.6, model ggml-large-v3-turbo.bin
OS / HW: macOS 26.6.2 (25G83), MacBook Air M4, 16 GB
Mode: live/realtime transcription (microphone), language = zh, VAD off
Symptom
About 39–40 minutes into a live recording, transcription silently stops. The app does not crash and does not show an error; new segments never appear, the recording buffer (temp/memo//folders/default//recordings/tmp.pcm) stops growing, and the session is not written to storage/local.db. Nothing recovers until Memo is fully quit and relaunched. The bug reproduces on every long session; the exact minute depends on how much log output whisper-server has produced.
Root cause (verified on a live hang)
whisper-server is spawned with the default stdio (three pipes). The main process only attaches a listener to stdout:
const Ze = spawn(Ge, Qe); // Qe = ["-m", model, "-l", lang, "--host", "0.0.0.0", "--port", port]
Ze.stdout?.on("data", Ke => { console.log([server] stdout: ${Ke}) });
// no Ze.stderr listener anywhere for this child
(app.asar, the whisper startWhisperServer / server-manager code. The stderr?.on("data") handler that does exist belongs to MemoRecorderServer, not to whisper-server.)
whisper-server writes per-request logs to stderr (operator(): processing '...' (N samples, S sec) ..., timings, etc.). Because nobody reads the pipe, the kernel socket buffer fills up. netstat -f unix on the hung process showed the stderr socket with Recv-Q 65494 (64 KB, i.e. full).
The next fprintf(stderr, ...) inside the /inference handler blocks forever while holding the inference mutex. All other httplib worker threads then block on that mutex, so every subsequent request from Memo hangs, times out client-side, and the pipeline stalls.
Thread sample of the hung whisper-server (all 1705/1705 samples):
Thread_209138 (holder)
httplib::Server::routing -> main::$_2::operator() + 2336
fprintf -> vfprintf_l -> __xvprintf -> __sflush -> _swrite -> __write_nocancel <-- blocked on stderr (fd 2, unix socket)
Thread_209136..209144 (8 workers)
httplib::Server::routing -> main::$_2::operator() + 84
std::mutex::lock -> _pthread_mutex_firstfit_lock_wait -> __psynch_mutexwait <-- waiting for the mutex above
Thread_209135: httplib::Server::listen_internal -> __accept (still accepting, so the backlog grows)
TCP state at the same moment (port 9588): one connection ESTABLISHED with 80 KB unread in the server's Recv-Q, one in CLOSE_WAIT with 268 KB unread (the client had already given up on it), and curl http://127.0.0.1:9588/ timed out.
This is the classic Node child_process pitfall documented in the Node docs: if the parent never consumes a piped stdio stream, the child can block once the pipe buffer is full.
Fix suggestions
Any one of these resolves it:
Attach a consumer: Ze.stderr?.on("data", ...) (log it, or .resume() to discard).
Spawn with stdio: ["ignore", "pipe", "ignore"] (or "inherit" for stderr).
Redirect the child's stderr to a rotating log file so users can send it with bug reports (bonus: this would also have made the failure visible).
Also worth doing: on /inference timeout, restart the whisper-server child instead of leaving the session in a state where the UI looks alive but nothing is transcribed.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in the whisper startWhisperServer/server-manager code in app.asar and inspect the spawn call and its existing stdout handling. Ensure whisper-server's stderr is consumed or redirected, then verify with a long live microphone recording that transcription continues past the failure point and the session is saved.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- audio-video-rtc, desktop
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100