agalwood/Motrix · error · HttpError
plugin.http.aborted
plugin.http.aborted
Error message
Request aborted by plugin
What it means
Before opening the undici request, the capability inspects opts.signal.aborted. If the plugin-supplied signal is already aborted it throws immediately, avoiding a wasted in-flight request and a confusing late abort. This is a preflight guard, distinct from the mid-flight aborts in errors 104-106.
Source
Thrown at src/core/plugin/capabilities/http.ts:286
}
// ---- Request body ----
let bodyPayload: string | Uint8Array | undefined
if (opts.body) {
const { bodyStr, contentType } = buildBodyPayload(opts.body)
bodyPayload = bodyStr
if (contentType && !reqHeaders['content-type']) {
reqHeaders['content-type'] = contentType
}
}
// ---- Abort plumbing ----
const internalCtrl = new AbortController()
const cleanup: (() => void)[] = []
if (opts.signal) {
if (opts.signal.aborted) {
throw new HttpError('plugin.http.aborted', 'Request aborted by plugin')
}
const onPluginAbort = () => internalCtrl.abort('plugin_abort')
opts.signal.addEventListener('abort', onPluginAbort, { once: true })
cleanup.push(() =>
opts.signal?.removeEventListener('abort', onPluginAbort)
)
}
const timer = setTimeout(() => internalCtrl.abort('timeout'), timeoutMs)
cleanup.push(() => clearTimeout(timer))
const doCleanup = () => {
for (const fn of cleanup) fn()
}
// ---- Per-hop loop ----
let currentUrl = opts.url
let redirected = falseView on GitHub (pinned to 1a708ee577)
Solutions
- Check signal.aborted before calling request() and short-circuit gracefully.
- Pass a fresh AbortController per logical request instead of reusing a consumed signal.
- If the abort is expected, treat this error as a normal cancellation outcome, not a fault.
Example fix
// before
controller.abort()
await http.request({ url, responseType: 'json', signal: controller.signal })
// after
if (controller.signal.aborted) return null
await http.request({ url, responseType: 'json', signal: controller.signal }) Defensive patterns
Strategy: validation
Validate before calling
if (opts.signal?.aborted) {
// already cancelled; do not call request()
return undefined
}
await http.request(opts) Type guard
function isSignalFresh(signal: AbortSignal | undefined): boolean {
return !signal?.aborted
} Try / catch
try {
await http.request(opts)
} catch (e) {
if (e instanceof HttpError && e.code === 'plugin.http.aborted' && /aborted by plugin/.test(e.message)) {
// pre-flight abort; expected during cancellation, exit gracefully
} else throw e
} Prevention
- Create a new AbortController per logical request; do not reuse consumed signals.
- Check signal.aborted at the top of any function that drives an HTTP call.
- Propagate cancellation as a normal return value, not an exception, in your plugin's API.
When it happens
Trigger: Passing an AbortSignal that was aborted before request() was called; reusing a signal from an already-cancelled task; a race where an outer controller aborts just before the call.
Common situations: Plugin's parent task was cancelled and the signal propagated down; user double-clicked cancel; signal stored on a long-lived object and reused after a prior abort.
Related errors
- manifest fetch failed: HTTP ${res.status}
- manifest too large: ${text.length} > ${max}
- plugin.http.invalid_url
- plugin.http.scheme_not_allowed
- plugin.http.response_type_required
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/a4ad857eebf5ed3d.
Report an issue: GitHub.