moeru-ai/airi · error · Error
Module ` ` was not found.
Error message
Module `${moduleId}` was not found. What it means
getModuleOrThrow (core.ts) looks a module up in the host's module registry by moduleId and throws this error when no such module exists. Modules enter the registry only via ctx.modules.register during an extension session and leave it on withdraw/unbind — stopExtension iterates owned modules and withdraws them all. So the error means either the id was never registered, or the owning extension's session already cleaned the module up.
Solutions
- Verify the module id against what the extension actually registers (ctx.modules.register({ id, ... })) — copy the exact string.
- If the owning extension was stopped/reloaded, re-establish its session and wait for module registration before invoking module-scoped APIs.
- List current modules from the registry (the host exposes listByOwner-style queries) instead of caching ids.
- On the caller side, catch this and refresh module listings rather than retrying the same dead id.
Example fix
// before
await host.callModule(sessionId, 'mocap-ui', input) // module id is 'ui.mocap'
// after
const modules = await host.listModulesFor(sessionId)
const target = modules.find(m => m.moduleId.endsWith('mocap'))
if (!target) throw new Error('mocap module not registered yet')
await host.callModule(sessionId, target.moduleId, input) Defensive patterns
Strategy: validation
Validate before calling
const modules = host.modules.listByOwner(sessionId) // or registry listing API
const target = modules.find(m => m.moduleId === moduleId)
if (!target) {
// module withdrawn or not yet registered: refresh and wait instead of invoking
}
await host.invokeModule(sessionId, moduleId, input) Try / catch
try {
await host.callModule(sessionId, moduleId, input)
} catch (error) {
if (errorMessageFrom(error).includes('` was not found.')) {
// stale module id (extension restarted): re-resolve from the live registry
return refreshModulesAndRetry(sessionId, input)
}
throw error
} Prevention
- Resolve module ids from the live registry at call time rather than caching them in UI state.
- After stopping/reloading an extension, wait for its modules to re-register before invoking module-scoped APIs.
- Use a shared constant for module ids shared between registration and callers so renames stay consistent.
When it happens
Trigger: Passing a moduleId string that was never registered (typo, renamed module); calling a host API that takes (sessionId, moduleId) after the extension stopped and its modules were withdrawn; referencing a module registered under a different session; races where a call arrives after module disposal.
Common situations: Host-side orchestration code holding module ids across extension restarts; UI dropdowns listing modules from a stale snapshot; renaming a module id in the extension without updating host-side callers.
Related errors
- Extension module ` ` is already registered for session .
- Tamagotchi extension tool not found
- gameletKit requires a host gamelet orchestration runtime.
- Kit ` ` is not registered.
- Module ` ` was not found.
AI-assisted analysis of moeru-ai/airi@438a067dde (2026-08-18).
Data as JSON: /api/errors/6b3b55ee9a9e7c0f.
Report an issue: GitHub.
Appendix: source
Thrown at packages/plugin-sdk/src/plugin-host/core.ts:583
contribution.install(this.installContext)
}
private async cleanupExtensionSession(session: ExtensionSession) {
session.phase = 'stopped'
for (const module of this.modules.listByOwner(session.id)) {
this.modules.withdraw(session.id, session.extension.id, module.moduleId)
this.modules.unbind(session.id, session.extension.id, module.moduleId)
}
await this.cleanupExtensionSessionModules(session)
await session.subscriptions.dispose()
this.extensionSessionService.remove(session.id)
}
private getModuleOrThrow(moduleId: string) {
const module = this.modules.get(moduleId)
if (!module) {
throw new Error(`Module \`${moduleId}\` was not found.`)
}
return module
}
private assertKitAvailableForRuntime(kitId: string, runtime: PluginRuntime) {
const kit = this.kits.get(kitId)
if (!kit) {
throw new Error(`Kit \`${kitId}\` is not registered.`)
}
if (!kit.runtimes.includes(runtime)) {
throw new Error(`Kit \`${kitId}\` is not available for runtime \`${runtime}\`.`)
}
return kit
}
View on GitHub (pinned to 438a067dde)