tonhowtf/omniget · error

Failed to parse ffprobe JSON

Error message

Failed to parse ffprobe JSON: {}

What it means

probe() runs ffprobe and parses its stdout as JSON. When serde_json cannot deserialize the output (e.g. empty, truncated, or non-JSON stdout), the parse error is wrapped into this anyhow error. It signals that ffprobe ran but its output was not the expected JSON stream.

Solutions

  1. Run `ffprobe -v quiet -print_format json -show_format <file>` manually on the failing file and inspect raw stdout.
  2. Verify the ffprobe on PATH (`which ffprobe`) is the real binary, not a script that prints extra output.
  3. Log output.stdout bytes on failure to confirm whether it is empty or malformed.
  4. Upgrade/reinstall ffprobe to a version that supports JSON output.

Example fix

// before
let json: serde_json::Value = serde_json::from_slice(&output.stdout)
    .map_err(|e| anyhow!("Failed to parse ffprobe JSON: {}", e))?;
// after
let stdout = String::from_utf8_lossy(&output.stdout);
if stdout.trim().is_empty() {
    anyhow::bail!("ffprobe returned empty output; check the file exists and ffprobe version");
}
let json: serde_json::Value = serde_json::from_str(stdout.trim())
    .map_err(|e| anyhow!("Failed to parse ffprobe JSON: {} (stdout: {:.200})", e, stdout))?
Defensive patterns

Strategy: try-catch

Validate before calling

let out = std::process::Command::new("ffprobe")
    .args(["-v", "quiet", "-print_format", "json", "-show_format", path])
    .output()
    .map_err(|e| format!("ffprobe not runnable: {e}"))?;
if !out.status.success() || out.stdout.trim().is_empty() {
    return Err("ffprobe output empty or failed".into());
}

Type guard

fn looks_like_ffprobe_json(stdout: &[u8]) -> bool {
    serde_json::from_slice::<serde_json::Value>(stdout)
        .map(|v| v.get("format").is_some())
        .unwrap_or(false)
}

Try / catch

match probe(path).await {
    Err(e) if e.to_string().contains("Failed to parse ffprobe JSON") => {
        log::warn!("ffprobe output unusable, skipping duration: {e}");
        Duration::ZERO // degraded mode
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling get_duration_us or convert on a file whose ffprobe invocation exits successfully but prints invalid/empty JSON to stdout (e.g. ffprobe binary replaced by a wrapper script, stdout polluted by other output, or a broken ffprobe build).

Common situations: A shim/wrapper around ffprobe adds banners to stdout; a corrupted or very old ffprobe build that doesn't support -print_format json; PATH pointing to the wrong binary; output truncated by an unusual locale/encoding wrapper.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


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

Appendix: source

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

            "-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())
        .unwrap_or("")
        .to_string();

    let format_long_name = format

View on GitHub (pinned to 8600b91f42)