tonhowtf/omniget · error
yt-dlp returned invalid JSON
Error message
yt-dlp returned invalid JSON: {} What it means
When yt-dlp exits successfully (status.success()), get_video_info parses its stdout as serde_json::Value. If stdout is not valid JSON (or is empty/garbage), this error is returned with the serde error. yt-dlp normally emits JSON when run with --dump-json/-j, so this indicates the command args or yt-dlp output changed.
Solutions
- Pin/update yt-dlp to a known-good version and ensure args still request JSON (--dump-json / -j)
- Check for a user yt-dlp config file injecting extra output and pass --ignore-config
- Capture and log the first bytes of stdout on failure to diagnose what was actually emitted
- Wrap parsing to include a stdout snippet in the error message for easier debugging
Example fix
// before
.map_err(|e| anyhow!("yt-dlp returned invalid JSON: {}", e))
// after
.map_err(|e| anyhow!("yt-dlp returned invalid JSON: {}; stdout head: {:?}",
e, String::from_utf8_lossy(&result.stdout.chars().take(200).collect::<Vec<_>>()))) Defensive patterns
Strategy: validation
Validate before calling
// After a successful exit, validate stdout looks like JSON before full parse
let out = String::from_utf8_lossy(&result.stdout);
let trimmed = out.trim_start();
if !(trimmed.starts_with('{') || trimmed.starts_with('[')) {
return Err(format!("yt-dlp output is not JSON (head: {:.120})", out));
} Type guard
fn looks_like_json(bytes: &[u8]) -> bool {
let s = String::from_utf8_lossy(bytes);
let t = s.trim_start();
t.starts_with('{') || t.starts_with('[')
} Try / catch
match get_video_info(url).await {
Err(e) if e.to_string().contains("invalid JSON") => {
// check yt-dlp version, --ignore-config, and re-run with logged stdout
}
other => other,
} Prevention
- Always pass explicit JSON flags (--dump-json/-j) rather than relying on defaults
- Run yt-dlp with --ignore-config so user config files cannot pollute stdout
- Pin and regularly update yt-dlp; test output shape after upgrades
- Log a stdout head snippet whenever parsing fails
When it happens
Trigger: yt-dlp exits 0 but stdout contains warnings, progress text, or HTML instead of pure JSON — e.g. a wrong --dump-json flag, an old/patched yt-dlp whose output format changed, output polluted by a config file, or stdout redirected/mixed with another source.
Common situations: A user's global yt-dlp.conf injecting non-JSON output; a wrapper script printing banners; version drift where yt-dlp no longer supports the flag the app passes; locale/encoding issues corrupting output.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- o JSON do yt-dlp não foi lido
- não achei appid para
- Expected a JSON array of cookie objects.
- nao esta listado em
- não consegui medir o loudness
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/cd56b15aac30dd41.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/core/ytdlp.rs:2351
anyhow!(
"Timeout fetching video info ({}s)",
VIDEO_INFO_PROCESS_TIMEOUT_SECS
)
})?
.map_err(|e| {
tracing::debug!("[perf] get_video_info took {:?}", _timer_start.elapsed());
anyhow!("Failed to run yt-dlp: {}", e)
})?;
tracing::debug!(
"[perf] get_video_info: yt-dlp process exited at {:?} (attempt {})",
_timer_start.elapsed(),
attempt + 1
);
if result.status.success() {
let json: serde_json::Value = serde_json::from_slice(&result.stdout)
.map_err(|e| anyhow!("yt-dlp returned invalid JSON: {}", e))?;
tracing::debug!("[perf] get_video_info took {:?}", _timer_start.elapsed());
return Ok(json);
}
let stderr = String::from_utf8_lossy(&result.stderr).to_string();
tracing::debug!(
"[yt-dlp info] stderr ({} bytes): {}",
stderr.len(),
stderr.trim()
);
let stderr_lower = stderr.to_lowercase();
if stderr_lower.contains("http error 429") {
rate_limit_429_increment();
let sanitized_url = sanitize_log_line(url);
tracing::warn!(
"[yt-429] rate limit in get_video_info: url={} attempt={}/{}",
sanitized_url,
attempt + 1,View on GitHub (pinned to 8600b91f42)