{"record":{"id":"855896a9ffe3497d","repo":"tonhowtf/omniget","slug":"no-stderr","errorCode":null,"errorMessage":"No stderr","messagePattern":"No stderr","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/ytdlp.rs","lineNumber":3471,"sourceCode":"            .stderr(Stdio::piped())\n            .kill_on_drop(true);\n        let mut child = cmd\n            .spawn()\n            .map_err(|e| anyhow!(\"Failed to start yt-dlp: {}\", e))?;\n        let registered_download_id = log_hook::current_download_id();\n        if let (Some(download_id), Some(pid)) = (registered_download_id, child.id()) {\n            register_download_process(download_id, pid);\n        }\n        tracing::debug!(\n            \"[perf] download_video: yt-dlp process spawned at {:?} (attempt {})\",\n            _timer_start.elapsed(),\n            attempt + 1\n        );\n\n        let _ = progress.send(ProgressUpdate::percent(-2.0)).await;\n\n        let stdout = child.stdout.take().ok_or_else(|| anyhow!(\"No stdout\"))?;\n        let stderr_pipe = child.stderr.take().ok_or_else(|| anyhow!(\"No stderr\"))?;\n\n        let lines = BufReader::new(stdout).lines();\n\n        let captured_path: Arc<Mutex<Option<PathBuf>>> = Arc::new(Mutex::new(None));\n        let line_reader = spawn_stdout_reader(\n            lines,\n            progress.clone(),\n            captured_path.clone(),\n            log_hook::current_download_id(),\n            is_audio_only,\n            _timer_start,\n            Some(boot_slot.clone()),\n        );\n\n        let stderr_log_id = log_hook::current_download_id();\n        let stderr_boot_slot = boot_slot.clone();\n        let stderr_reader = tokio::spawn(async move {\n            let mut buf = String::new();","sourceCodeStart":3453,"sourceCodeEnd":3489,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/ytdlp.rs#L3453-L3489","documentation":"Raised when `child.stderr.take()` returns None for the spawned yt-dlp child. Stderr is piped at spawn and consumed by a background reader task that captures error text; a missing handle breaks that capture and aborts the download with this message.","triggerScenarios":"Reaching ytdlp.rs:3471 (or the duplicate at 4095) with no stderr pipe attached to the child — only possible if the spawn setup deviated from `Stdio::piped()` for stderr or the handle was already consumed.","commonSituations":"Modified/forked build where stderr was redirected elsewhere (e.g. inherited or to a file); code paths that take stderr twice.","solutions":["Ensure `cmd.stderr(Stdio::piped())` is set before spawn and stderr is taken only once.","Restart the download attempt.","If using a custom build, revert local modifications to the spawn configuration."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"fn take_stderr(child: &mut tokio::process::Child) -> anyhow::Result<tokio::process::ChildStderr> {\n    child.stderr.take().ok_or_else(|| anyhow!(\"stderr was not piped or already taken\"))\n}","tryCatchPattern":"let Some(stderr_pipe) = child.stderr.take() else {\n    tracing::error!(\"stderr pipe missing — spawn configuration bug\");\n    return Err(anyhow!(\"No stderr\"));\n};","preventionTips":["Set Stdio::piped() for stderr at the same site as stdout configuration.","Never consume child.stderr in two places; use Arc<Mutex<>> if multiple readers are needed.","Cover the spawn helper with a unit test asserting both pipes are Some."],"tags":["rust","process-pipe","ytdlp","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}