tonhowtf/omniget · error

Access denied (403). The video may be private or region-rest

Error message

Access denied (403). The video may be private or region-restricted.

What it means

translate_ytdlp_error in src-tauri/omniget-core/src/core/ytdlp.rs:4432 maps yt-dlp stderr containing 'http error 403' or 'forbidden' to this user-facing message. The remote media server refused the request with HTTP 403, meaning the downloader authenticated or wasn't authorized to fetch the stream/resource. The library throws it to surface a server-side access denial instead of a raw yt-dlp trace.

Solutions

  1. Import site cookies (Settings → Cookies) so the request carries an authenticated session.
  2. Update yt-dlp to the latest version, then retry the download.
  3. Check whether the video is genuinely region-restricted; use a matching network/region if legitimate.
  4. Retry later from a different network/IP if the host has rate- or IP-blocked you.

Example fix

// before: anonymous download of an age/region-gated video
// omniget invocation passes no cookie file
let out = run_ytdlp(&["-f", "best", url])?; // stderr: 'http error 403'

// after: supply cookies so the CDN sees an authorized session
let out = run_ytdlp(&["--cookies", &cookies_path, "-f", "best", url])?;
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check the URL with a lightweight request before invoking the downloader
const res = await fetch(url, { method: 'HEAD' });
if (res.status === 403) throw new Error('Site returns 403: import cookies or check region before starting this download.');

Try / catch

try {
  await download(url);
} catch (e) {
  if (is403AccessDenied(e)) {
    // prompt user to import cookies / switch network, then retry once
    await importCookies();
    await download(url);
  } else throw e;
}

Prevention

When it happens

Trigger: yt-dlp stderr contains 'http error 403' or the substring 'forbidden' while resolving or downloading a URL; typically a stream request rejected by the host (YouTube/CDN) due to missing or expired session tokens, region restrictions, or IP-level blocks.

Common situations: Downloading age-restricted or region-locked videos without cookies; expired extracted stream URLs that no longer match the client IP; scraping at scale from a datacenter IP that the host blocks; a stale yt-dlp version producing requests the CDN rejects as forbidden.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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

Appendix: source

Thrown at src-tauri/omniget-core/src/core/ytdlp.rs:4432

fn translate_ytdlp_error(stderr: &str) -> anyhow::Error {
    let lower = stderr.to_lowercase();

    if lower.contains("errno 22")
        && (lower.contains("textiowrapper")
            || lower.contains("encoding=")
            || lower.contains("exception ignored"))
    {
        return anyhow!(
            "Console encoding error (non-UTF-8 locale). Update yt-dlp in Settings → Dependencies, or run `chcp 65001` in a terminal and reopen the app."
        );
    }

    if lower.contains("http error 429") {
        return anyhow!("Server returned error 429 (too many requests). Try again later.");
    }
    if lower.contains("http error 403") || lower.contains("forbidden") {
        return anyhow!("Access denied (403). The video may be private or region-restricted.");
    }
    if lower.contains("sign in to confirm")
        || lower.contains("login required")
        || stderr.contains("请先登录")
        || stderr.contains("需要登录")
        || stderr.contains("登录后可")
        || stderr.contains("仅登录用户")
        || lower.contains("this video is only available") && lower.contains("members")
    {
        return anyhow!(
            "This video requires login. Import cookies for this site in Settings → Cookies, then retry."
        );
    }
    if lower.contains("invalid data found when processing input") {
        return anyhow!(
            "Downloaded streams are DRM-protected and cannot be merged. This content is not supported."
        );
    }

View on GitHub (pinned to 8600b91f42)