agalwood/Motrix · error · Error
manifest fetch failed: HTTP ${res.status}
Error message
manifest fetch failed: HTTP ${res.status} What it means
Plain Error thrown by fetchManifest when the HTTP response status is not ok (res.ok is false). The message includes the status code so the caller can distinguish 404 (wrong URL), 401/403 (auth), 429 (rate limit), 5xx (origin failure), etc. The fetch follows redirects automatically.
Source
Thrown at src/core/media/manifest-fetcher.ts:18
const DEFAULT_MAX = 5 * 1024 * 1024
export async function fetchManifest(
url: string,
opts: {
headers?: Record<string, string>
fetchImpl?: typeof fetch
maxBytes?: number
} = {}
): Promise<string> {
const doFetch = opts.fetchImpl ?? fetch
const res = await doFetch(url, {
method: 'GET',
headers: opts.headers ?? {},
redirect: 'follow',
})
if (!res.ok) {
throw new Error(`manifest fetch failed: HTTP ${res.status}`)
}
const text = await res.text()
const max = opts.maxBytes ?? DEFAULT_MAX
if (text.length > max) {
throw new Error(`manifest too large: ${text.length} > ${max}`)
}
return text
}
View on GitHub (pinned to 1a708ee577)
Solutions
- Read the status code from the message and act: 401/403 -> fix headers/auth; 404 -> refresh the URL; 429 -> back off and retry; 5xx -> retry with jitter.
- Pass opts.headers with the required Authorization/Referer/User-Agent/Cookie for the CDN.
- For pre-signed URLs, regenerate them closer to fetch time (they typically expire in minutes–hours).
- Wrap fetchManifest in a retry-with-backoff for 429/5xx, capped at a few attempts.
Example fix
// before
const text = await fetchManifest(url, {})
// after — pass auth headers and retry on transient failures
async function fetchWithRetry(url, headers, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try {
return await fetchManifest(url, { headers })
} catch (e) {
if (i === attempts - 1 || !/HTTP (429|5\d\d)/.test(String(e))) throw e
await new Promise(r => setTimeout(r, 500 * 2 ** i))
}
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Validate URL reachability before fetchManifest if you want an early signal.
function looksLikeManifestUrl(url: string): boolean {
return /\.(m3u8|mpd|json)(\?|$)/i.test(url)
}
if (!looksLikeManifestUrl(url)) {
throw new Error(`URL does not look like a manifest: ${url}`)
} Try / catch
try {
const text = await fetchManifest(url, { headers })
} catch (e) {
const status = /HTTP (\d{3})/.exec(String(e))?.[1]
if (status === '404') { /* refresh URL */ }
else if (status === '401' || status === '403') { /* fix auth */ }
else if (status === '429' || /^5\d\d$/.test(status ?? '')) { /* back off and retry */ }
else throw e
} Prevention
- Pass required headers (Authorization, Referer, User-Agent, Cookie) via opts.headers.
- Refresh pre-signed URLs immediately before fetching them.
- Wrap fetchManifest in a bounded retry with exponential backoff for 429/5xx.
When it happens
Trigger: Calling fetchManifest(url, opts) where the origin returns any non-2xx status. Commonly 401/403 when opts.headers lacks a required Authorization or Cookie; 404 for a stale/expired signed URL; 429 from a CDN rate limiter; 5xx from an origin outage.
Common situations: Expired pre-signed S3/CloudFront URLs; missing or wrong Referer/Origin/User-Agent headers required by the CDN; geo-blocks returning 451; origin maintenance windows; rate-limited scrapers.
Related errors
- manifest too large: ${text.length} > ${max}
- unsupported-live
- unsupported-encryption
- unsupported-master
- unsupported-master
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/6016c49444cadb94.
Report an issue: GitHub.