{"record":{"id":"e782e4d2938f8ac2","repo":"agalwood/Motrix","slug":"pluginruntimefault-e782e4","errorCode":"PluginRuntimeFault","errorMessage":"plugin chain aborted: ${result.reason}","messagePattern":"plugin chain aborted: (.+?)","errorType":"exception","errorClass":"AppError","httpStatus":null,"severity":"error","filePath":"src/core/task/create-task-handler.ts","lineNumber":430,"sourceCode":"      }\n      const result = await deps.orchestrator.runBeforeCreateHttp(ctxDto, taskId)\n      log.info(\n        {\n          taskId,\n          aborted: result.aborted === true,\n          rewrittenUris: result.aborted ? undefined : result.final.uris,\n          contributors: result.aborted ? undefined : result.contributors,\n        },\n        'beforeCreate hook chain result'\n      )\n      if (result.aborted) {\n        await deps.auditLog?.log({\n          type: 'chain.abort',\n          hook: 'beforeCreate',\n          taskId,\n          reason: result.reason,\n        })\n        throw new AppError(\n          ErrorCode.PluginRuntimeFault,\n          `plugin chain aborted: ${result.reason}`\n        )\n      }\n      // Apply merged outputs back to the create params. mergeChain is\n      // well-defined for the slot keys we care about; absent keys keep\n      // the user's input intact. Conditionality matches the old code:\n      //   - uris: ALWAYS overwritten (the chain always produces a final set)\n      //   - headers: only when the chain produced headers (else keep req)\n      //   - proxy: TRUTHY check (an empty-string proxy must NOT overwrite)\n      params.uris = [...result.final.uris]\n      if (result.final.headers.length > 0) {\n        params.headers = Object.fromEntries(\n          result.final.headers.map((h) => [h.name, h.value])\n        )\n      }\n      if (result.final.proxy) {\n        params.proxy = result.final.proxy","sourceCodeStart":412,"sourceCodeEnd":448,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/task/create-task-handler.ts#L412-L448","documentation":"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.","triggerScenarios":"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.","commonSituations":"User installed a filtering/governance plugin that vetoes certain sources; enterprise policy plugin enforcing allowlists; a buggy plugin aborting unintentionally; plugin config too strict.","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."],"exampleFix":"// before\nif (result.aborted) {\n  throw new AppError(ErrorCode.PluginRuntimeFault, `plugin chain aborted: ${result.reason}`)\n}\n\n// after — pass through the aborting plugin id for actionable triage\nif (result.aborted) {\n  throw new AppError(ErrorCode.PluginRuntimeFault, `plugin '${result.abortedBy}' aborted: ${result.reason}`)\n}","handlingStrategy":"try-catch","validationCode":"// No code-side pre-check: the abort is plugin policy. Inspect installed plugins' beforeCreate logic.\nconst blockers = await deps.pluginRegistry.listBeforeCreateBlockers()\nif (blockers.length) ui.warn('A governance plugin may block this source')","typeGuard":"function isPluginChainAbort(e) { return e instanceof AppError && e.code === ErrorCode.PluginRuntimeFault && /chain aborted/.test(e.message) }","tryCatchPattern":"try {\n  await handleCreateTask(req, deps, opts)\n} catch (e) {\n  if (e instanceof AppError && e.code === ErrorCode.PluginRuntimeFault && /chain aborted/.test(e.message)) {\n    ui.notifyUser('plugin_blocked_create', { reason: e.message })\n    return\n  }\n  throw e\n}","preventionTips":["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."],"tags":["plugin","create","policy","hook","governance"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}