tonhowtf/omniget · error

ffprobe failed

Error message

ffprobe failed: {}

What it means

probe launched ffprobe successfully, but it exited with a non-zero status; the error message includes ffprobe's captured stderr for diagnosis. This is the standard signal that ffprobe could not parse/inspect the given media file. Callers get_duration_us and convert propagate this failure.

Solutions

  1. Read the stderr in the error message — it names the exact codec/container problem (e.g. 'Invalid data found when processing input').
  2. Verify the input file exists, is non-empty, and download completed (not a .part file) before calling probe.
  3. Re-download the media if it's truncated or corrupt.
  4. Check file read permissions and that the path is a regular file; update ffmpeg/ffprobe for newer formats.

Example fix

// before
if !output.status.success() {
    let stderr = String::from_utf8_lossy(&output.stderr);
    return Err(anyhow!("ffprobe failed: {}", stderr));
}
// after — validate the input before probing
if !path.exists() || metadata.len() == 0 {
    return Err(anyhow!("cannot probe missing or empty file: {}", path.display()));
}
if !output.status.success() {
    let stderr = String::from_utf8_lossy(&output.stderr);
    return Err(anyhow!("ffprobe failed ({}): {}", output.status, stderr));
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate the media file before asking ffprobe to parse it
let md = tokio::fs::metadata(path).await?;
if !md.is_file() { bail!("not a file: {}", path.display()); }
if md.len() == 0 { bail!("empty file: {}", path.display()); }
// Optionally skip files still being written:
if path.extension().map_or(false, |e| e == "part") { bail!("download still in progress: {}", path.display()); }

Try / catch

// The library already embeds ffprobe's stderr — surface it to the user and classify common cases
match probe(path).await {
    Err(e) if e.to_string().contains("ffprobe failed") => {
        let msg = e.to_string();
        if msg.contains("Invalid data found") || msg.contains("No such file") {
            Err(anyhow!("file is corrupt, truncated, or not a media file: {msg}"))
        } else {
            Err(e)
        }
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling probe on a file ffprobe cannot handle: nonexistent path, empty or truncated download (.part file), unsupported/corrupt container, or a path ffprobe can't read (permissions).

Common situations: Probing a file before a download finished (still .part); interrupted download left a truncated file; probing a path with broken permissions; probing HTML error pages saved as media files; DRM-protected or exotic codecs.

Related errors


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

Appendix: source

Thrown at src-tauri/omniget-core/src/core/ffmpeg.rs:134

    let output = crate::core::process::command("ffprobe")
        .args([
            "-v",
            "quiet",
            "-print_format",
            "json",
            "-show_format",
            "-show_streams",
            &path.to_string_lossy(),
        ])
        .stdout(std::process::Stdio::piped())
        .stderr(std::process::Stdio::piped())
        .output()
        .await
        .map_err(|e| anyhow!("Failed to run ffprobe: {}", e))?;

    if !output.status.success() {
        let stderr = String::from_utf8_lossy(&output.stderr);
        return Err(anyhow!("ffprobe failed: {}", stderr));
    }

    let json: serde_json::Value = serde_json::from_slice(&output.stdout)
        .map_err(|e| anyhow!("Failed to parse ffprobe JSON: {}", e))?;

    let format = json
        .get("format")
        .ok_or_else(|| anyhow!("Missing 'format' field"))?;

    let duration_seconds = format
        .get("duration")
        .and_then(|v| v.as_str())
        .and_then(|s| s.parse::<f64>().ok())
        .unwrap_or(0.0);

    let format_name = format
        .get("format_name")
        .and_then(|v| v.as_str())

View on GitHub (pinned to 8600b91f42)