{"record":{"id":"313291dafb0b7c40","repo":"tonhowtf/omniget","slug":"access-denied-403-the-video-may-be-private-or-region","errorCode":null,"errorMessage":"Access denied (403). The video may be private or region-restricted.","messagePattern":"Access denied \\(403\\)\\. The video may be private or region-restricted\\.","errorType":"exception","errorClass":null,"httpStatus":403,"severity":"error","filePath":"src-tauri/omniget-core/src/core/ytdlp.rs","lineNumber":4432,"sourceCode":"\nfn translate_ytdlp_error(stderr: &str) -> anyhow::Error {\n    let lower = stderr.to_lowercase();\n\n    if lower.contains(\"errno 22\")\n        && (lower.contains(\"textiowrapper\")\n            || lower.contains(\"encoding=\")\n            || lower.contains(\"exception ignored\"))\n    {\n        return anyhow!(\n            \"Console encoding error (non-UTF-8 locale). Update yt-dlp in Settings → Dependencies, or run `chcp 65001` in a terminal and reopen the app.\"\n        );\n    }\n\n    if lower.contains(\"http error 429\") {\n        return anyhow!(\"Server returned error 429 (too many requests). Try again later.\");\n    }\n    if lower.contains(\"http error 403\") || lower.contains(\"forbidden\") {\n        return anyhow!(\"Access denied (403). The video may be private or region-restricted.\");\n    }\n    if lower.contains(\"sign in to confirm\")\n        || lower.contains(\"login required\")\n        || stderr.contains(\"请先登录\")\n        || stderr.contains(\"需要登录\")\n        || stderr.contains(\"登录后可\")\n        || stderr.contains(\"仅登录用户\")\n        || lower.contains(\"this video is only available\") && lower.contains(\"members\")\n    {\n        return anyhow!(\n            \"This video requires login. Import cookies for this site in Settings → Cookies, then retry.\"\n        );\n    }\n    if lower.contains(\"invalid data found when processing input\") {\n        return anyhow!(\n            \"Downloaded streams are DRM-protected and cannot be merged. This content is not supported.\"\n        );\n    }","sourceCodeStart":4414,"sourceCodeEnd":4450,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/ytdlp.rs#L4414-L4450","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Import site cookies (Settings → Cookies) so the request carries an authenticated session.","Update yt-dlp to the latest version, then retry the download.","Check whether the video is genuinely region-restricted; use a matching network/region if legitimate.","Retry later from a different network/IP if the host has rate- or IP-blocked you."],"exampleFix":"// before: anonymous download of an age/region-gated video\n// omniget invocation passes no cookie file\nlet out = run_ytdlp(&[\"-f\", \"best\", url])?; // stderr: 'http error 403'\n\n// after: supply cookies so the CDN sees an authorized session\nlet out = run_ytdlp(&[\"--cookies\", &cookies_path, \"-f\", \"best\", url])?;","handlingStrategy":"retry","validationCode":"// Pre-check the URL with a lightweight request before invoking the downloader\nconst res = await fetch(url, { method: 'HEAD' });\nif (res.status === 403) throw new Error('Site returns 403: import cookies or check region before starting this download.');","typeGuard":null,"tryCatchPattern":"try {\n  await download(url);\n} catch (e) {\n  if (is403AccessDenied(e)) {\n    // prompt user to import cookies / switch network, then retry once\n    await importCookies();\n    await download(url);\n  } else throw e;\n}","preventionTips":["Keep site cookies imported and fresh for gated platforms","Keep yt-dlp updated so signed stream URLs are accepted by CDNs","Avoid bulk downloads from datacenter IPs that hosts flag"],"tags":["network","http","ytdlp","auth"],"backgroundTag":"http-error-response","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}