most-furniture-64179
03/03/2026, 8:59 PMlivekit:dev / npx tsx ... dev) and hitting Ctrl+C, we get an unhandled EPIPE crash:
Error: write EPIPE
at target._send (node:internal/child_process:877:20)
at target.send (node:internal/child_process:751:19)
at <anonymous> (node_modules/@livekit/agents/dist/ipc/inference_proc_lazy_main.js:81:21)
followed by:
libc++abi: terminating due to uncaught exception of type std::__1::system_error: mutex lock failed: Invalid argument
What's happening: SIGINT hits both the CLI and inference processes simultaneously. The CLI shuts down the worker and closes the IPC pipe, but the inference process (inference_proc_lazy_main.js:81) still tries to process.send() back — pipe is gone, unhandled EPIPE, which cascades into the native RTC addon mutex crash.
Running through a process manager (like turborepo's turbo watch) that sequences the shutdown signals avoids this since the IPC pipes are still intact when children communicate. So it's not blocking us day-to-day, but it does make local dev mode less clean.
SDK version: @livekit/agents 1.0.47, Node 22.14.0, macOS
Seems like wrapping that process.send in a try-catch or adding a process.on('error') handler in the inference proc would fix ittall-airline-16756
03/03/2026, 8:59 PMtall-airline-16756
03/03/2026, 8:59 PMprocess.send().
What you’ve described matches the root cause: the CLI closes the IPC pipe while the inference process still calls process.send(), which raises an unhandled EPIPE and then cascades into the native RTC addon mutex failure. Running under a process manager that sequences shutdown avoids the race, which confirms this is a shutdown race-condition in local dev mode.
What you can try locally (quick mitigations)
• Add a process.on('error', ...) handler in the inference process to catch IPC errors and prevent uncaught exceptions.
• Wrap the process.send(...) call in a try/catch and ignore EPIPE (or perform a safe shutdown path) so the inference process can exit cleanly.
• Before calling process.send(), check process.connected (true if the IPC channel is open) and skip sending if it’s false.
Why those help
• process.connected prevents attempts to write to a closed pipe.
• A guarded process.send() or an error handler prevents the unhandled exception that triggers the native addon crash, so shutdown completes cleanly even with simultaneous SIGINT.
Next steps from our side
• I’ll escalate this to the engineering team for a code fix in the agents package so dev-mode shutdown is resilient by default (for example, adding the process.connected check and an IPC error handler, or surrounding process.send() with try/catch). This aligns with improving developer experience in dev mode.
If you’d like, please share:
• The small snippet around the process.send() call (the file inference_proc_lazy_main.js near line 81) you’re seeing locally — that will help us propose the exact patch and speed up the fix.
• Whether you prefer a temporary patch you can apply locally (I can suggest an edit) or you’d rather wait for an SDK patch from the team.
Thanks again for the clear report and mitigation suggestion — this is exactly the kind of edge case we want to make robust for local development.
Sources: Agent speech and audio | LiveKit Documentation | Voice AI quickstart | LiveKit Documentation | Server startup modes | LiveKit Documentationmost-furniture-64179
03/03/2026, 9:00 PMtall-airline-16756
03/03/2026, 9:01 PMprocess.send() in your local inference_proc_lazy_main.js (near line 81), or
• Any additional reproduction steps, timestamps, or a minimal repo/commands that reliably reproduce the crash.
Otherwise I’ll escalate now with the details you’ve already provided. Which would you prefer?