tonhowtf/omniget · error

No stderr

Error message

No stderr

What it means

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.

Solutions

  1. Ensure `cmd.stderr(Stdio::piped())` is set before spawn and stderr is taken only once.
  2. Restart the download attempt.
  3. If using a custom build, revert local modifications to the spawn configuration.
Defensive patterns

Strategy: type-guard

Type guard

fn take_stderr(child: &mut tokio::process::Child) -> anyhow::Result<tokio::process::ChildStderr> {
    child.stderr.take().ok_or_else(|| anyhow!("stderr was not piped or already taken"))
}

Try / catch

let Some(stderr_pipe) = child.stderr.take() else {
    tracing::error!("stderr pipe missing — spawn configuration bug");
    return Err(anyhow!("No stderr"));
};

Prevention

When it happens

Trigger: 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.

Common situations: Modified/forked build where stderr was redirected elsewhere (e.g. inherited or to a file); code paths that take stderr twice.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/855896a9ffe3497d. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/core/ytdlp.rs:3471

            .stderr(Stdio::piped())
            .kill_on_drop(true);
        let mut child = cmd
            .spawn()
            .map_err(|e| anyhow!("Failed to start yt-dlp: {}", e))?;
        let registered_download_id = log_hook::current_download_id();
        if let (Some(download_id), Some(pid)) = (registered_download_id, child.id()) {
            register_download_process(download_id, pid);
        }
        tracing::debug!(
            "[perf] download_video: yt-dlp process spawned at {:?} (attempt {})",
            _timer_start.elapsed(),
            attempt + 1
        );

        let _ = progress.send(ProgressUpdate::percent(-2.0)).await;

        let stdout = child.stdout.take().ok_or_else(|| anyhow!("No stdout"))?;
        let stderr_pipe = child.stderr.take().ok_or_else(|| anyhow!("No stderr"))?;

        let lines = BufReader::new(stdout).lines();

        let captured_path: Arc<Mutex<Option<PathBuf>>> = Arc::new(Mutex::new(None));
        let line_reader = spawn_stdout_reader(
            lines,
            progress.clone(),
            captured_path.clone(),
            log_hook::current_download_id(),
            is_audio_only,
            _timer_start,
            Some(boot_slot.clone()),
        );

        let stderr_log_id = log_hook::current_download_id();
        let stderr_boot_slot = boot_slot.clone();
        let stderr_reader = tokio::spawn(async move {
            let mut buf = String::new();

View on GitHub (pinned to 8600b91f42)