moeru-ai/airi · error · Error
Module `${moduleId}` was not found.
Error message
Module `${moduleId}` was not found. What it means
Thrown by `BindingRegistry.transition()` in the plugin-host kit-api-bindings service when no binding record exists for the supplied `moduleId`. Transitions (activate, withdraw, reconfigure, custom lifecycle moves) operate only on already-registered bindings, so a missing entry means the module was never bound or has already been unbound. The check happens before ownership or state-machine validation, so it always fires first for an unknown module id.
Source
Thrown at packages/plugin-sdk/src/plugin-host/runtimes/shared/services/kit-api-bindings.ts:419
* Use when:
* - A caller needs a custom lifecycle transition beyond the convenience helpers
*
* Expects:
* - `owner` matches the stored binding owner
* - `state`, when provided, is legal from the current lifecycle state
*
* Returns:
* - The next canonical binding record written back into the registry
*/
transition(
owner: BindingOwnerIdentity,
moduleId: string,
state?: BindingState,
patch: BindingUpdatePatch<C> = {},
) {
const current = this.bindings.get(moduleId)
if (!current) {
throw new Error(`Module \`${moduleId}\` was not found.`)
}
if (
current.ownerSessionId !== owner.ownerSessionId
|| current.ownerExtensionId !== owner.ownerExtensionId
) {
throw createOwnershipError(
moduleId,
{
ownerSessionId: current.ownerSessionId,
ownerExtensionId: current.ownerExtensionId,
},
owner,
)
}
const nextState = state ?? current.state
if (!allowedBindingTransitions[current.state].includes(nextState)) {View on GitHub (pinned to 27111382b4)
Solutions
- Ensure the module is registered with the registry (bind/init) before calling transition/withdraw — check that the same `moduleId` was used at registration time.
- Guard the call: use `registry.get(moduleId)` (or the equivalent lookup) and skip the transition if it returns undefined, since teardown may have already removed it.
- If this fires during shutdown/teardown paths, treat a missing binding as already-cleaned-up and make the caller idempotent rather than throwing.
- Audit for stale module-id references after a hot-reload or session restart that resets the registry.
Example fix
// before
registry.withdraw(sessionId, extensionId, moduleId)
// after
if (registry.get(moduleId)) {
registry.withdraw(sessionId, extensionId, moduleId)
} Defensive patterns
Strategy: validation
Validate before calling
if (!registry.get(moduleId)) {
// skip transition or initialize the binding first
return
}
registry.transition(owner, moduleId, state, patch) Type guard
function isBound(moduleId: string): boolean {
return registry.get(moduleId) !== undefined
} Try / catch
try {
registry.transition(owner, moduleId, state, patch)
} catch (e) {
if (e instanceof Error && e.message.startsWith('Module `') && e.message.endsWith('` was not found.')) {
// binding already removed during teardown — treat as success
return
}
throw e
} Prevention
- Always pair transition/withdraw calls with a prior bind/init registration for the same moduleId.
- Make teardown paths idempotent: check registry.get(moduleId) before transitioning.
- Use a single source of truth for module ids to avoid typos and stale references.
- After a reload/session reset, re-register bindings before reusing cached module ids.
When it happens
Trigger: Calling `registry.transition(owner, moduleId, state, patch)`, or any convenience helper that delegates to it (e.g. `withdraw(ownerSessionId, ownerExtensionId, moduleId)`), with a `moduleId` that was never registered via the bind/init path or that has already been removed by `unbind()`. Also triggered by stale references held after a hot-reload or session teardown that cleared the registry.
Common situations: Plugin host code that reuses cached module ids across a reload/teardown cycle; calling `withdraw()` during shutdown after the binding was already physically removed; typo or mismatched module id between the bind site and the transition site; tests that call transition directly without first initializing a binding.
Related errors
- Godot stage is not running.
- Cannot declare permissions for unknown plugin "${extensionId
- Cannot grant permissions to unknown plugin "${extensionId}".
- Godot stage bridge is not connected.
- Previous Godot stage process is still shutting down. Retry a
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/b0fd6b5e5d3a38f1.
Report an issue: GitHub.