agalwood/Motrix · critical · Error
plugin.lifecycle.activation_capability_violation
plugin.lifecycle.activation_capability_violation
Error message
effectful call ${capability}.${method} during activation What it means
During module evaluation (currentPhase === 'activation') only registration-only calls are allowed: hooks.beforeCreate/beforeFinalize/afterComplete/onError, commands.register, and lifecycle.onActivate/onDeactivate. Any effectful call (http, fs.task, fs.storage, storage, notify, ffmpeg, crypto, config, metadata, commands.execute, i18n.t) is fatal — the worker sends a 'fatal' message and throws, killing the plugin. (log.* is an explicit exception and is permitted at top level.)
Source
Thrown at src/core/plugin/host/quick-js-worker.ts:94
const registeredDeactivateHandlers: QuickJSHandle[] = []
function send(msg: WorkerToHost): void {
port.postMessage(msg)
}
function assertEffectfulAllowed(capability: string, method: string): void {
if (currentPhase !== 'activation') return
if (classify(capability, method) !== 'effectful') return
violationFatal = {
code: 'plugin.lifecycle.activation_capability_violation',
message: `effectful call ${capability}.${method} during activation`,
}
send({
type: 'fatal',
code: violationFatal.code,
message: violationFatal.message,
})
throw new Error(violationFatal.message)
}
async function callHost(
capability: string,
method: string,
args: unknown[]
): Promise<unknown> {
const id = nextCallId++
return new Promise((resolve, reject) => {
pendingCalls.set(id, (resp) => {
if (resp.type !== 'response') return
if (resp.ok) {
resolve(resp.result)
} else {
const e: Error & { code?: string } = new Error(resp.error.message)
e.code = resp.error.code
reject(e)
}View on GitHub (pinned to 1a708ee577)
Solutions
- Move effectful work into onActivate or a hook/command handler; keep module top-level to registration only.
- If a value is needed at top-level, compute it lazily inside a hook instead.
- Use log.* (the permitted exception) only for diagnostics, not for state-changing work.
Example fix
// before — effectful call at module top level
const cfg = await config.get('key')
hooks.beforeCreate(async (ctx) => { /* uses cfg */ })
// after — defer into a hook
hooks.beforeCreate(async (ctx) => {
const cfg = await config.get('key')
/* ... */
}) Defensive patterns
Strategy: validation
Validate before calling
import { classify } from '../capabilities/classification'
// at plugin authoring/review time: assert top-level calls are registration-only
function assertTopLevelAllowed(capability: string, method: string): void {
const c = classify(capability, method)
if (c !== 'registration-only') {
throw new Error(`${capability}.${method} is ${c}; move it out of module top level into onActivate or a hook`)
}
} Type guard
import { isRegistrationOnly } from '../capabilities/classification'
function isAllowedAtTopLevel(capability: string, method: string): boolean {
return isRegistrationOnly(capability, method) || capability === 'log'
} Try / catch
// this error is fatal and kills the plugin; there is no in-process recovery.
// wrap setup work so it never runs at module scope:
hooks.beforeCreate(async () => {
try { /* effectful setup */ } catch (e) { /* handle inside the hook */ }
}) Prevention
- Keep module top-level to registration-only calls (hooks.*, commands.register, lifecycle.*).
- Move any effectful setup into onActivate or a hook/command handler.
- Remember log.* is the only effectful-classified exception permitted at top level.
When it happens
Trigger: A plugin calls notify.show, http.get, storage.set, ffmpeg.*, crypto.*, config.get, or i18n.t at top-level module scope rather than inside a registered hook/command handler or onActivate.
Common situations: Plugin runs setup work at import time; reads config during module init; opens a connection in module scope; calls i18n.t to build a top-level constant.
Related errors
- PluginActivationCapExceeded
- plugin.lifecycle.secrets_seed_missing
- plugin.lifecycle.deactivate_timeout
- plugin.fs.task.not_available_outside_hook
- plugin.metadata.not_available_outside_hook
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/5c090c0135535fa5.
Report an issue: GitHub.