tonhowtf/omniget · critical

Failed to start yt-dlp

Error message

Failed to start yt-dlp: {}

What it means

This error is raised when `Command::spawn()` fails to launch the yt-dlp executable. The library built the command with piped stdout/stderr and kill_on_drop, then attempted to spawn the child process; the OS refused to start it (typically because the binary path does not exist or is not executable). The underlying OS error is interpolated into the message.

Solutions

  1. Verify the yt-dlp binary exists at the resolved path and is executable (`which yt-dlp`, `chmod +x`).
  2. Reinstall/update yt-dlp via Settings → Dependencies in the app.
  3. Check the configured custom yt-dlp path setting and correct or clear it.
  4. On Windows/macOS, un-quarantine the binary (antivirus / `xattr -d com.apple.quarantine yt-dlp`).
  5. Confirm PATH contains the directory holding yt-dlp if a bare name is used.
Defensive patterns

Strategy: validation

Validate before calling

let path = resolve_ytdlp_path()?;
if !path.exists() { return Err(anyhow!("yt-dlp binary not found at {}", path.display())); }
#[cfg(unix)]
{
    use std::os::unix::fs::PermissionsExt;
    let mode = std::fs::metadata(&path)?.permissions().mode();
    if mode & 0o111 == 0 { return Err(anyhow!("yt-dlp binary is not executable")); }
}

Prevention

When it happens

Trigger: Calling the download path in src-tauri/omniget-core/src/core/ytdlp.rs (~line 3457) when the resolved yt-dlp binary is missing, not executable, deleted mid-run, or blocked from exec (permissions, AppArmor/SELinux, quarantine flag).

Common situations: yt-dlp was never installed or was auto-updated and the binary path changed; the configured custom yt-dlp path in Settings → Dependencies points to a nonexistent file; the binary lost its +x bit; on Windows the executable is quarantined by antivirus.

Related errors


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

Appendix: source

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

            &args,
            attempt as u32 + 1,
            max_attempts as u32,
            yt_args.player_client.clone().filter(|_| is_youtube),
            effective_fragments,
            engine,
            false,
        );
        let attempt_started = std::time::Instant::now();

        let boot_slot = hold_boot_slot("download").await;
        let mut cmd = ytdlp_command(ytdlp);
        cmd.args(&args)
            .stdout(Stdio::piped())
            .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));

View on GitHub (pinned to 8600b91f42)