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.proxyView on GitHub (pinned to 1a708ee577)
Solutions
- Read `result.reason` (and the `chain.abort` audit entry) to identify which plugin aborted and why.
- Adjust the offending plugin's configuration (allowlist the source, raise quota, supply credentials).
- If the abort is unintended, debug/disable the suspect plugin and check its beforeCreate logic.
- 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
- Read the chain.abort audit log to identify which plugin aborted and why.
- Configure/allowlist sources in policy plugins instead of removing them.
- Debug/disable a suspect plugin if its aborts are unintended.
- Surface abort reasons to the user with the offending plugin name.
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
- plugin.commands.id_out_of_namespace
- plugin.command.not_declared_in_manifest
- plugin.commands.not_found
- plugin.commands.access_denied
- plugin.lifecycle.secrets_seed_missing
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/e782e4d2938f8ac2.
Report an issue: GitHub.