tonhowtf/omniget · error · anyhow::Error

{}

Error message

{}

What it means

After running yt-dlp in JSON mode, `ytdlp_json` scans stdout for a line starting with '{'. If none is found, it treats the run as failed and raises an error whose message is the shortened stderr reason (via `short_reason`). This surfaces yt-dlp's own failure output (e.g. video unavailable, login required) as the error.

Solutions

  1. Read the `{}` message: it is the shortened stderr from yt-dlp; fix the cause it names (URL, login, network).
  2. Update yt-dlp to the latest version (`yt-dlp -U` or pip install -U yt-dlp) — TikTok extractors change often.
  3. Verify the video URL opens in a browser and is public.
  4. If cookies/session are required, pass the session cookie file to yt-dlp.

Example fix

// before
$ yt-dlp -j https://www.tiktok.com/@u/video/123
ERROR: video unavailable
// after
$ yt-dlp -U   # update extractors, then retry with a valid public URL
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate the URL shape before invoking yt-dlp
if !url.contains("tiktok.com") { return Err(anyhow!("URL não parece ser do TikTok")); }

Try / catch

match ytdlp_json(&args).await {
    Ok(v) => Ok(v),
    Err(e) if e.to_string().contains("unavailable") => warn_user_and_skip(),
    Err(e) => { update_ytdlp().await; Err(e) },
}

Prevention

When it happens

Trigger: yt-dlp exits with no JSON on stdout — unsupported/removed video, network failure, age/login restriction, bad URL, or a yt-dlp version whose output format changed; stdout is empty while stderr carries the reason.

Common situations: Private or region-blocked TikTok video; yt-dlp outdated for a changed TikTok page structure; bot-check/captcha page returned instead of media; invalid or malformed URL passed in.

Related errors


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

Appendix: source

Thrown at src-tauri/omniget-core/src/core/tools/tiktok/mod.rs:757

    let bin = ytdlp_binary().await?;
    let mut cmd = crate::core::ytdlp::ytdlp_command(&bin);
    cmd.args(args)
        .stdin(std::process::Stdio::null())
        .stdout(std::process::Stdio::piped())
        .stderr(std::process::Stdio::piped());
    let out = cmd
        .output()
        .await
        .map_err(|e| anyhow!("não foi possível iniciar o yt-dlp: {}", e))?;
    let text = String::from_utf8_lossy(&out.stdout);
    let line = text
        .lines()
        .map(|l| l.trim())
        .find(|l| l.starts_with('{'))
        .unwrap_or("");
    if line.is_empty() {
        let tail = String::from_utf8_lossy(&out.stderr).to_string();
        return Err(anyhow!("{}", short_reason(&tail)));
    }
    serde_json::from_str(line).map_err(|e| anyhow!("o JSON do yt-dlp não foi lido: {}", e))
}

// ───────────────────────── formatação ─────────────────────────

pub fn fmt_utc(ts: i64) -> String {
    chrono::DateTime::from_timestamp(ts, 0)
        .map(|d| d.format("%Y-%m-%d %H:%M UTC").to_string())
        .unwrap_or_default()
}

/// Escapa um campo de CSV do jeito que a planilha espera.
pub fn csv_escape(s: &str) -> String {
    if s.contains([',', '"', '\n', '\r']) {
        format!("\"{}\"", s.replace('"', "\"\""))
    } else {
        s.to_string()

View on GitHub (pinned to 8600b91f42)