{"record":{"id":"4ce66e6eefb3b773","repo":"tonhowtf/omniget","slug":"yt-dlp","errorCode":null,"errorMessage":"yt-dlp: {}","messagePattern":"yt-dlp: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/ytdlp.rs","lineNumber":4520,"sourceCode":"        .unwrap_or(\"\")\n        .trim();\n\n    let msg = if !last_error_line.is_empty() {\n        last_error_line\n            .strip_prefix(\"ERROR: \")\n            .or_else(|| last_error_line.strip_prefix(\"ERROR:\"))\n            .or_else(|| last_error_line.strip_prefix(\"error: \"))\n            .unwrap_or(last_error_line)\n    } else {\n        let trimmed = stderr.trim();\n        if trimmed.len() > 300 {\n            &trimmed[..300]\n        } else {\n            trimmed\n        }\n    };\n\n    anyhow!(\"yt-dlp: {}\", msg)\n}\n\npub fn get_rate_limit_stats() -> serde_json::Value {\n    serde_json::json!({\n        \"rate_limit_429_count\": RATE_LIMIT_429_COUNT.load(Ordering::Relaxed)\n    })\n}\n\n/// Corpo de uma linha do nosso `--progress-template`.\n///\n/// O `download:` no início do template é consumido pelo yt-dlp como o *tipo*\n/// da template (`[TYPES:]TEMPLATE`), então a linha impressa começa direto\n/// pelo percentual: `  0.5%|eta:NA|spd:…`. Reconhecemos a linha pelos campos\n/// que só o nosso template tem (`|eta:` e `|spd:`), com o prefixo opcional\n/// para o caso de o yt-dlp mudar de ideia.\nfn template_body(line: &str) -> Option<&str> {\n    let t = line.trim();\n    if let Some(rest) = t.strip_prefix(\"download:\") {","sourceCodeStart":4502,"sourceCodeEnd":4538,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/ytdlp.rs#L4502-L4538","documentation":"Fallback error for classified yt-dlp failures: when none of the specific stderr patterns matched, the wrapper takes the last error line from yt-dlp's stderr (truncated to 300 chars) and wraps it as anyhow!(\"yt-dlp: {}\"). It represents any unmatched yt-dlp failure.","triggerScenarios":"Any yt-dlp exit failure whose stderr does not match earlier patterns: extractor exceptions, HTTP 403/429 on fragments, postprocessing errors, disk-space errors, or yt-dlp crashes.","commonSituations":"New site-side changes producing novel errors, yt-dlp version too old for a site, transient CDN errors, or very long/verbose stderr hiding the root cause.","solutions":["Read the embedded yt-dlp message after the 'yt-dlp:' prefix — it is the actual root cause","Update yt-dlp to the latest release; most extractor errors are fixed upstream","Re-run manually with `yt-dlp -v <url>` for verbose diagnostics","Check rate-limit stats (get_rate_limit_stats) if the message mentions HTTP 429"],"exampleFix":"// before\n// unmatched stderr -> error swallowed or generic panic\n// after\nlet last_error_line = stderr.lines().rev().find(|l| !l.trim().is_empty());\nanyhow!(\"yt-dlp: {}\", msg)","handlingStrategy":"try-catch","validationCode":"// keep yt-dlp current; stale extractors cause most unmatched errors\nlet version = tokio::process::Command::new(&ytdlp).arg(\"--version\").output().await?;\nif is_outdated(&version) { prompt_update(); }","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().starts_with(\"yt-dlp: \") => {\n        log::error!(\"unclassified yt-dlp failure: {e}\");\n        show_verbose_log_hint(&e);\n    }\n    other => other?,\n}","preventionTips":["Always log the full stderr for unmatched failures","Prompt users to update yt-dlp when errors repeat","Offer a 'copy verbose log' action for bug reports","Track 429s via get_rate_limit_stats and back off"],"tags":["yt-dlp","fallback","stderr","diagnostics"],"backgroundTag":"api-error-response","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}