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
- Read the stderr in the error message — it names the exact codec/container problem (e.g. 'Invalid data found when processing input').
- Verify the input file exists, is non-empty, and download completed (not a .part file) before calling probe.
- Re-download the media if it's truncated or corrupt.
- 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
- Only probe files whose download has fully completed — never .part or in-flight files.
- Check file existence and non-zero size before probing; an HTML error page saved as .mp4 will fail here.
- Surface the stderr text (already included in the error) — it pinpoints the codec/container problem.
- Re-download media that fails probing rather than feeding truncated files downstream to convert/get_duration_us.
- Keep ffmpeg/ffprobe updated for newer codecs and containers.
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
- ffmpeg returned code
- Failed to run ffprobe
- não consegui medir a duração
- Track sem soundcloud_id
- SoundCloud nao retornou URL
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)