{"record":{"id":"aa96ea788a209644","repo":"Kuberwastaken/claurst","slug":"voice-thread-runtime","errorCode":null,"errorMessage":"voice thread runtime","messagePattern":"voice thread runtime","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-rust/crates/core/src/voice.rs","lineNumber":251,"sourceCode":"                .error_message()\r\n                .unwrap_or_else(|| \"Voice unavailable\".to_string());\r\n            let _ = event_tx.send(VoiceEvent::Error(msg.clone())).await;\r\n            return Err(anyhow::anyhow!(msg));\r\n        }\r\n\r\n        self.is_recording.store(true, Ordering::SeqCst);\r\n\r\n        let is_recording = self.is_recording.clone();\r\n        let config = self.config.clone();\r\n\r\n        // cpal::Stream is !Send, so we can't use tokio::spawn (which requires Send).\r\n        // Instead, spin up a dedicated OS thread with its own single-threaded tokio\r\n        // runtime so the stream stays local to that thread throughout its lifetime.\r\n        std::thread::spawn(move || {\r\n            let rt = tokio::runtime::Builder::new_current_thread()\r\n                .enable_all()\r\n                .build()\r\n                .expect(\"voice thread runtime\");\r\n            rt.block_on(async move {\r\n                match record_and_transcribe(is_recording, event_tx.clone(), config).await {\r\n                    Ok(()) => {}\r\n                    Err(e) => {\r\n                        let _ = event_tx.send(VoiceEvent::Error(e.to_string())).await;\r\n                    }\r\n                }\r\n            });\r\n        });\r\n\r\n        Ok(())\r\n    }\r\n\r\n    /// Stop recording.  The transcription request is sent immediately after\r\n    /// the audio capture loop exits.\r\n    pub async fn stop_recording(&mut self) -> anyhow::Result<()> {\r\n        self.is_recording.store(false, Ordering::SeqCst);\r\n        Ok(())\r","sourceCodeStart":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/Kuberwastaken/claurst/blob/b0637c97ec34144387cbf2f74f65df6d16a6cef1/src-rust/crates/core/src/voice.rs#L233-L269","documentation":"This panic wraps creation of a dedicated single-threaded tokio runtime on the voice recording OS thread: `tokio::runtime::Builder::new_current_thread().enable_all().build().expect(...)`. Runtime build only fails if tokio's I/O and timer drivers cannot be initialized (e.g. epoll/kqueue unavailable), so the code treats it as unrecoverable on the voice thread.","triggerScenarios":"Calling `start_recording` when the spawned thread's `tokio::runtime::Builder::new_current_thread().enable_all().build()` returns Err — driver setup failure such as running under a sandbox without eventfd/epoll support, or resource exhaustion creating the driver.","commonSituations":"Running inside restrictive sandboxes (some seccomp profiles, WebAssembly-ish or受限 environments) where epoll_create/eventfd are blocked; heavily restricted CI runners; exotic platforms unsupported by mio.","solutions":["Verify the process runs on a supported platform where mio can create its event loop (Linux epoll / macOS kqueue); check seccomp/container policies blocking `epoll_create1`/`eventfd`.","Test voice startup early with a minimal current_thread runtime probe to surface driver issues at boot.","Ensure tokio's `rt`, `time`, and `net` features are enabled consistently across the workspace (mixed tokio versions can cause driver conflicts).","Make the thread send `VoiceEvent::Error(\"failed to start voice runtime: ...\")` instead of panicking, so the UI degrades gracefully."],"exampleFix":"// before\nlet rt = tokio::runtime::Builder::new_current_thread()\n    .enable_all()\n    .build()\n    .expect(\"voice thread runtime\");\n// after\nlet rt = match tokio::runtime::Builder::new_current_thread().enable_all().build() {\n    Ok(rt) => rt,\n    Err(e) => {\n        let _ = event_tx.blocking_send(VoiceEvent::Error(format!(\"voice runtime init failed: {e}\")));\n        return;\n    }\n};","handlingStrategy":"try-catch","validationCode":"// Probe driver availability before enabling voice:\nfn voice_runtime_ok() -> bool {\n    tokio::runtime::Builder::new_current_thread()\n        .enable_all()\n        .build()\n        .is_ok()\n}","typeGuard":null,"tryCatchPattern":"// The panic happens on the spawned thread — observe it via the event channel:\nwhile let Some(ev) = voice_events.recv().await {\n    match ev {\n        VoiceEvent::Error(msg) if msg.contains(\"voice thread runtime\") => {\n            // disable voice feature, show user-facing message\n        }\n        _ => {}\n    }\n}","preventionTips":["Check platform/seccomp support for epoll/eventfd before enabling voice","Keep tokio versions unified across the workspace to avoid driver conflicts","Send VoiceEvent::Error instead of panicking on the dedicated thread","Probe runtime construction at startup when voice is configured on"],"tags":["rust","tokio","async-runtime","voice","panic"],"backgroundTag":"runtime-initialization-failed","analyzedSha":"b0637c97ec34144387cbf2f74f65df6d16a6cef1","analyzedAt":"2026-09-10T00:24:58.650Z","contentChangedAt":"2026-09-10T00:24:58.650Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}