agalwood/Motrix · error · AppError

PluginRuntimeFault

PluginRuntimeFault

Error message

plugin chain aborted: ${result.reason}

What it means

Thrown by create-task-handler.ts:430 when the `beforeCreate` plugin hook chain returns `result.aborted === true`. A plugin in the chain explicitly aborted creation with a `reason` (policy rejection, blocked URL, quota, validation). An audit log entry of type `chain.abort` is recorded before the throw. This is intentional plugin-driven rejection, not a runtime crash.

Source

Thrown at src/core/task/create-task-handler.ts:430

      }
      const result = await deps.orchestrator.runBeforeCreateHttp(ctxDto, taskId)
      log.info(
        {
          taskId,
          aborted: result.aborted === true,
          rewrittenUris: result.aborted ? undefined : result.final.uris,
          contributors: result.aborted ? undefined : result.contributors,
        },
        'beforeCreate hook chain result'
      )
      if (result.aborted) {
        await deps.auditLog?.log({
          type: 'chain.abort',
          hook: 'beforeCreate',
          taskId,
          reason: result.reason,
        })
        throw new AppError(
          ErrorCode.PluginRuntimeFault,
          `plugin chain aborted: ${result.reason}`
        )
      }
      // Apply merged outputs back to the create params. mergeChain is
      // well-defined for the slot keys we care about; absent keys keep
      // the user's input intact. Conditionality matches the old code:
      //   - uris: ALWAYS overwritten (the chain always produces a final set)
      //   - headers: only when the chain produced headers (else keep req)
      //   - proxy: TRUTHY check (an empty-string proxy must NOT overwrite)
      params.uris = [...result.final.uris]
      if (result.final.headers.length > 0) {
        params.headers = Object.fromEntries(
          result.final.headers.map((h) => [h.name, h.value])
        )
      }
      if (result.final.proxy) {
        params.proxy = result.final.proxy

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Read `result.reason` (and the `chain.abort` audit entry) to identify which plugin aborted and why.
  2. Adjust the offending plugin's configuration (allowlist the source, raise quota, supply credentials).
  3. If the abort is unintended, debug/disable the suspect plugin and check its beforeCreate logic.
  4. Disable or uninstall the plugin if its policy no longer applies.

Example fix

// before
if (result.aborted) {
  throw new AppError(ErrorCode.PluginRuntimeFault, `plugin chain aborted: ${result.reason}`)
}

// after — pass through the aborting plugin id for actionable triage
if (result.aborted) {
  throw new AppError(ErrorCode.PluginRuntimeFault, `plugin '${result.abortedBy}' aborted: ${result.reason}`)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// No code-side pre-check: the abort is plugin policy. Inspect installed plugins' beforeCreate logic.
const blockers = await deps.pluginRegistry.listBeforeCreateBlockers()
if (blockers.length) ui.warn('A governance plugin may block this source')

Type guard

function isPluginChainAbort(e) { return e instanceof AppError && e.code === ErrorCode.PluginRuntimeFault && /chain aborted/.test(e.message) }

Try / catch

try {
  await handleCreateTask(req, deps, opts)
} catch (e) {
  if (e instanceof AppError && e.code === ErrorCode.PluginRuntimeFault && /chain aborted/.test(e.message)) {
    ui.notifyUser('plugin_blocked_create', { reason: e.message })
    return
  }
  throw e
}

Prevention

When it happens

Trigger: A security/policy plugin's beforeCreate hook calls abort because the URI matches a blocklist; a quota plugin aborts when the user is over limit; an auth plugin aborts on missing credentials; a content-filter plugin rejects the filename/type.

Common situations: User installed a filtering/governance plugin that vetoes certain sources; enterprise policy plugin enforcing allowlists; a buggy plugin aborting unintentionally; plugin config too strict.

Related errors


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