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 = false

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Check signal.aborted before calling request() and short-circuit gracefully.
  2. Pass a fresh AbortController per logical request instead of reusing a consumed signal.
  3. 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

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


AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12). Data as JSON: /api/errors/a4ad857eebf5ed3d. Report an issue: GitHub.