rich-art-45250
12/22/2025, 4:31 PMtall-airline-16756
12/22/2025, 4:31 PMtall-airline-16756
12/22/2025, 4:31 PMpredict_end_of_turn step taking >> 50–160ms).
• Network or public inference endpoint congestion causing queued/slow requests.
Immediate troubleshooting steps
1. Check instance type and CPU credit balance (if using t3/t4): verify CPUCreditBalance in CloudWatch or move off burstable instances.
2. Inspect per-component latencies: measure STT, LLM token streaming time, and TTS generation time to identify the slow stage.
3. Confirm turn-detection latency: if predict_end_of_turn consistently >500ms, that points to resource throttling.
4. If using public inference endpoints, check for request queuing or increased network latency; consider moving inference closer to your app.
5. If possible, enable streaming-capable models and stream tokens to start TTS earlier (slice TTS by sentence) to reduce end-to-end lag.
Short-term mitigations
• Switch to dedicated CPU instance families (e.g., c5, m5, c6i) to avoid CPU credit exhaustion.
• If you must keep burstable instances, enable t3.unlimited and monitor costs/credits.
• Optimize pipeline to stream partial LLM output to TTS as it arrives.
If you’d like, I can help narrow this down — which of these apply to your setup?
• Are you running on AWS t3/t4 instances?
• Do you control the STT/LLM/TTS providers (which models/providers)?
• Can you share recent metrics for CPU credits and per-stage latencies (STT/LLM/TTS/EOU)? Sources: Introducing LiveKit Inference: A unified model interface for voice AI | Troubleshooting Latency and Timeout Errors with Turn Detection on AWS | Live Conversations with AI using ChatGPT and WebRTCrich-art-45250
12/22/2025, 4:34 PMtall-airline-16756
12/22/2025, 4:35 PMfs.read(), fs.createReadStream() or reopened/closed descriptors used in callbacks/promises.
◦ Python: wave.open, file objects used outside with blocks or in background tasks.
• Ensure graceful shutdown handling: add SIGTERM/SIGINT handlers to stop background jobs, close streams, and await pending I/O before exit.
• Wrap file I/O with error handling to avoid unhandled rejections:
◦ Node: check callback/Promise errors from fs APIs.
◦ Python: catch `OSError`/`ValueError` around reads.
• Avoid using closed descriptors: ensure you don’t close a stream and then use its buffer later asynchronously.
• Check for lifecycle events: LiveKit Cloud can scale/drain instances — verify your agent cleans up on termination to avoid operations during container shutdown.
• If you read audio files repeatedly, consider loading into memory (buffer) once and reuse instead of reopening files concurrently.
Immediate quick fixes to try
• Add try/catch (or .catch) around all file reads and stream operations to prevent unhandled rejections.
• Add logging right before every file read to capture which path/descriptor is being used when the error occurs.
• Add a process.on('unhandledRejection') handler (Node) that logs stack/context to help identify caller.
Questions to help me pinpoint the cause
1. Which runtime is the agent using (Node.js or Python) and which version?
2. Does the crash happen during startup, during a session, or on shutdown/scale events?
3. Can you paste a small code snippet that performs file I/O or any stack trace lines that reference files/streams?
4. Are you seeing any autoscaling/drain or SIGTERM logs just before the EBADF events?
If you share the runtime and a short snippet or the log lines immediately preceding the crash, I’ll point to the exact change to prevent it and a minimal patch you can apply.
Sources: Deploying to production | LiveKit Docs | Agent server options | LiveKit docs | Playing Audio | LiveKit docs | Examples | livekit/livekit_composite | DeepWiki | Still Have Questionsrich-art-45250
12/22/2025, 4:35 PMtall-airline-16756
12/22/2025, 4:35 PMEBADF: bad file descriptor, read and earlier logs showing inference lag. Let’s tackle the EBADF first (likely causing the crash), then confirm any inference tuning you still need.
Node.js — targeted checks and fixes for EBADF
1. Add centralized handlers to capture context
• Add these to log stack/context (temporary) so we can see where reads originate:
process.on('unhandledRejection', (err) => {
console.error('UNHANDLED REJECTION', err);
});
process.on('uncaughtException', (err) => {
console.error('UNCAUGHT EXCEPTION', err);
});
1. Identify problematic file/stream usage
• Search for fs.read, fs.readFile, fs.createReadStream, or any native addon that reads file descriptors.
• Add a brief log before each read/stream open to capture the filename/path and a unique operation id.
1. Fix common async/stream mistakes
• Do not use file descriptors after closing them. If you call fd = fs.openSync(...) ensure you fs.closeSync(fd) only after all async work with that fd is done.
• When using streams, ensure you don’t call stream.destroy() or stream.close() while other async handlers still assume it’s readable.
• Example safe pattern for read streams:
const rs = fs.createReadStream(path);
rs.on('error', (err) => console.error('readstream error', err));
rs.on('open', () => console.log('opened', path));
rs.on('end', () => console.log('ended', path));
1. Guard against operations during shutdown/drain
• Handle termination signals and stop starting new reads; wait for inflight I/O to finish:
let shuttingDown = false;
process.on('SIGTERM', async () => {
shuttingDown = true;
console.log('SIGTERM received — waiting for I/O to complete');
await shutdownCleanup(); // close streams, stop jobs
process.exit(0);
});
• In read paths, skip/abort if shuttingDown is true.
1. Wrap all file I/O promises and callbacks
• Ensure every Promise-based fs call has a .catch() and callbacks check err. Unhandled promise rejections can mask origin.
fs.promises.readFile(path)
.then(data => { /* use data */ })
.catch(err => console.error('readFile failed', path, err));
1. Check third-party native modules
• If you use native addons or binaries (audio libs, ffmpeg wrappers), they may close fds unexpectedly. Add try/catch and vendor-specific error handling.
1. Reduce concurrent file descriptor usage
• If you open many files simultaneously, hitting OS fd limits or race conditions can expose EBADF. Use a pool/queue to limit concurrency.
Quick debugging steps to run now
• Deploy a build with the centralized handlers and extra logs around all read/open calls.
• Reproduce the crash and paste the new logs showing which file/path and stack triggered EBADF.
• If feasible, run the agent locally with the same workload to reproduce under a debugger.
Short checklist for “inference is slower than realtime” (reminder)
• Confirm you are not on burstable instances (t3/t4); CPU throttling causes big latency spikes.
• Measure per-stage latency (STT, LLM token streaming, TTS, and predict_end_of_turn) to find the slow component.
• Stream LLM output and start TTS on partial text to reduce end-to-end latency.
• Consider moving to provisioned inference (LiveKit Inference) or dedicated instance types (c5, m5).
Next steps from me
• If you can paste: (A) the new logs after adding the handlers, (B) snippets where you open/read audio or files, and (C) whether the crash aligns with shutdown/scale events — I’ll point to the exact line to patch and provide a minimal code fix.
Would you like me to show examples for common patterns you use (fs.promises, stream, or native addon)?