agalwood/Motrix · warning · AppError
PluginActivationCapExceeded
PluginActivationCapExceeded
Error message
plugin.lifecycle.activation_cap_exceeded
What it means
Thrown by ActivationDispatcher.admit() when the two-pass eviction (idle-LRU then tier-aware) could not free enough slots to fit the newly matching inactive plugins within maxActive. Before throwing it emits the 'plugin.activation_cap_exceeded' event carrying the unfittable plugin ids, then raises AppError with ErrorCode.PluginActivationCapExceeded.
Source
Thrown at src/core/plugin/host/activation-dispatcher.ts:129
async admit(
matchingInactive: string[],
event: HostActivationEvent
): Promise<void> {
const needed = this.host.activeIds().length + matchingInactive.length
if (needed <= this.maxActive) {
for (const id of matchingInactive) {
await this.host.activate(id)
}
return
}
const slotsNeeded = needed - this.maxActive
const criticalSet = this.deriveCriticalSet(event)
const freed = await this.runEviction(slotsNeeded, criticalSet)
if (freed < slotsNeeded) {
this.emitter?.emit('plugin.activation_cap_exceeded', {
unfittable: matchingInactive.filter((id) => !this.host.isActive(id)),
})
throw new AppError(
ErrorCode.PluginActivationCapExceeded,
'plugin.lifecycle.activation_cap_exceeded'
)
}
for (const id of matchingInactive) {
await this.host.activate(id)
}
}
/**
* Derive the set of plugin ids that must not be evicted for this event.
*
* T14 placeholder: always returns an empty set. The tier-ordering in
* `runEviction` already protects critical roles by evicting 'audit' first.
* T15 will replace this with real in-flight tracking via
* `PluginHost.activeWithInFlightHook(taskId)` once TaskManager wires the
* HookOrchestrator.
*/View on GitHub (pinned to 1a708ee577)
Solutions
- Raise maxActive in the ActivationDispatcher constructor options (or DEFAULT_MAX_ACTIVE_PLUGINS).
- Narrow plugin activationEvents so fewer plugins match a given event (avoid '*' / broad onTaskType).
- Disable or uninstall plugins that are not needed to reduce steady-state active count.
- If you control the emitter, handle 'plugin.activation_cap_exceeded' to surface the unfittable ids to the user rather than letting the throw propagate.
Example fix
// before
new ActivationDispatcher(registry, host, { maxActive: 4 })
// after
new ActivationDispatcher(registry, host, { maxActive: 16 }) Defensive patterns
Strategy: try-catch
Validate before calling
// Before dispatch, estimate whether admission will fit.
const projected = host.activeIds().length + matchingInactive.length
if (projected > maxActive) {
// pre-emptively narrow the matching set or surface a warning
} Try / catch
try {
await dispatcher.dispatch(event)
} catch (e) {
if (e instanceof AppError && e.code === ErrorCode.PluginActivationCapExceeded) {
// degraded mode: log and continue; the unfittable ids were emitted
return
}
throw e
} Prevention
- Subscribe to the 'plugin.activation_cap_exceeded' event to surface unfittable ids to the user.
- Keep activationEvents narrow so simultaneous matches stay within maxActive.
- Disable plugins the user does not need to lower steady-state active count.
When it happens
Trigger: host.activeIds().length + matchingInactive.length > maxActive AND runEviction(slotsNeeded, criticalSet) returns fewer than slotsNeeded freed slots. This happens when too few plugins are idle (>60s) and too few sit in evictable tiers (audit/enrich/post-process/resolve/pre-resolve) outside the critical set.
Common situations: maxActive / DEFAULT_MAX_ACTIVE_PLUGINS configured too low for the workload; many plugins all match onStartup or onTaskType simultaneously; all active plugins are recently busy (idle < 60s) so Pass 1 finds nothing; critical set (deriveCriticalSet) is over-broad in a future revision; user has installed many plugins with overlapping activationEvents.
Related errors
- plugin.lifecycle.activation_capability_violation
- plugin.lifecycle.secrets_seed_missing
- plugin.lifecycle.deactivate_timeout
- PluginRuntimeFault
- plugin.fs.task.not_available_outside_hook
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/df616a602dc01c1c.
Report an issue: GitHub.