agalwood/Motrix · error · HttpError
plugin.http.redirect_not_allowed
plugin.http.redirect_not_allowed
Error message
redirect: 'error' set; refusing to follow ${status} to ${location} What it means
The caller explicitly set redirect:'error', and the server returned a 3xx with a Location header. The body is dumped (drained) and the request fails instead of following or returning the redirect. This is the strict opt-in mode for plugins that want to forbid silent redirects (e.g. to avoid SSRF or auth stripping).
Source
Thrown at src/core/plugin/capabilities/http.ts:396
const location = response.headers.location
const isRedirect = status >= 300 && status < 400 && location
if (isRedirect) {
if (redirect === 'manual') {
// Surface the 3xx as-is.
return await buildResponse<R>(
response,
opts.responseType,
maxBodyBytes,
internalCtrl,
currentUrl,
redirected,
doCleanup
)
}
if (redirect === 'error') {
await response.body.dump?.()
throw new HttpError(
'plugin.http.redirect_not_allowed',
`redirect: 'error' set; refusing to follow ${status} to ${location}`
)
}
// redirect === 'follow'
if (hops >= MAX_REDIRECTS) {
await response.body.dump?.()
throw new HttpError(
'plugin.http.too_many_redirects',
`Too many redirects (>${MAX_REDIRECTS})`
)
}
await response.body.dump?.()
const loc = Array.isArray(location) ? (location[0] ?? '') : location
const nextUrl = new URL(loc, currentUrl)
// Re-validate the scheme on every hop. checkScheme only ran on the
// initial URL, so a 3xx Location to file:// (or any non-http scheme)
// would otherwise escape the allowlist.View on GitHub (pinned to 1a708ee577)
Solutions
- Switch to redirect:'follow' (the default) if following is acceptable.
- Switch to redirect:'manual' to inspect the 3xx and the Location yourself.
- Fix the upstream URL so it resolves directly without a redirect.
Example fix
// before
await http.request({ url, responseType: 'json', redirect: 'error' })
// after
await http.request({ url, responseType: 'json', redirect: 'follow' }) Defensive patterns
Strategy: validation
Validate before calling
const redirect = expectRedirect ? 'follow' : 'manual'
await http.request({ ...opts, redirect }) Try / catch
try {
await http.request({ ...opts, redirect: 'error' })
} catch (e) {
if (e instanceof HttpError && e.code === 'plugin.http.redirect_not_allowed') {
// expected; switch to 'manual' or 'follow' depending on policy
} else throw e
} Prevention
- Default to 'follow' unless you have an explicit security reason to forbid redirects.
- Use 'manual' when you need to inspect the 3xx and the Location yourself.
- Resolve redirecting URLs at the base URL once, rather than per request.
When it happens
Trigger: Passing redirect:'error' and hitting any URL that returns 301/302/303/307/308; an endpoint that always redirects (e.g. http->https, trailing-slash normalization); a short-link service.
Common situations: Security-hardened plugin that disallows redirects; misconfigured base URL pointing at a redirecting endpoint; ops changed a URL to redirect.
Related errors
- plugin.http.scheme_not_allowed
- manifest too large: ${text.length} > ${max}
- plugin.http.too_many_redirects
- unsupported-kind
- manifest fetch failed: HTTP ${res.status}
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/dc7b37c1ba24a87a.
Report an issue: GitHub.