moeru-ai/airi · error · Error
Kit ` ` is not registered.
Error message
Kit `${kitId}` is not registered. What it means
Thrown by ExtensionHost.assertKitAvailableForRuntime when a binding announcement (announceBinding / bindExtensionKitModule) references a kitId that is not in the host's kit registry. The host only knows kits that were registered via host.registerKit(...) (or the install context's registerKit) before the plugin tries to bind a module to them. It is a startup/ordering contract: kit contracts must exist before extensions can announce modules against them.
Solutions
- Register the kit on the same host instance before starting/announcing: host.registerKit({ kitId: 'my-kit', runtimes: [...], capabilities: [...] }) and only then start the extension or call announceBinding.
- Verify the exact kitId string against the registry: const kit = host.getKit('my-kit'); if (!kit) ... — fix typos/casing so KitRef.id and KitDescriptor.kitId match.
- Inspect host.listKits() (optionally host.listKits(runtime)) at startup to log which kits are actually registered before extensions run.
- If the kit was deliberately unregistered, make sure no in-flight extension session still tries to announce modules for it (stop the session first).
Example fix
// before
const session = await host.start(manifest) // extension setup calls announceBinding('my-kit')
host.registerKit({ kitId: 'my-kit', runtimes: ['node'], capabilities: [] }) // too late
// after
host.registerKit({ kitId: 'my-kit', runtimes: ['node'], capabilities: [] })
const session = await host.start(manifest) // kit already registered when the extension announces Defensive patterns
Strategy: validation
Validate before calling
// Before announceBinding / starting the extension:
const kit = host.getKit('my-kit')
if (!kit) {
host.registerKit({ kitId: 'my-kit', runtimes: ['node'], capabilities: [] })
}
// Optionally sanity-check the full registry:
const registered = host.listKits().map(k => k.kitId)
if (!registered.includes('my-kit')) throw new Error(`Register kits first. Have: ${registered.join(', ')}`) Type guard
const isKitRegistered = (host: ExtensionHost, kitId: string): boolean => host.getKit(kitId) !== undefined
Try / catch
try {
host.announceBinding(sessionId, { kitId: 'my-kit', moduleId: 'm-1', ... })
} catch (error) {
if (error instanceof Error && error.message.startsWith('Kit `') && error.message.includes('is not registered.')) {
// register the kit, then retry the announce once
}
throw error
} Prevention
- Register all kit descriptors during host construction, before any host.start call.
- Derive kitId from a single shared constant used by both KitDescriptor and KitRef to prevent drift.
- Log host.listKits() at bootstrap in dev builds to catch ordering mistakes early.
When it happens
Trigger: Calling host.announceBinding(sessionId, { kitId: 'my-kit', ... }) or the kit-helper path bindExtensionKitModule before host.registerKit({ kitId: 'my-kit', ... }); passing a kitId with a typo or different casing than the registered descriptor; registering the kit asynchronously (e.g. after an await) while the extension setup already announced; the kit was unregistered via unregisterKit during teardown but a late announce still runs.
Common situations: Bootstrapping code that starts an extension before the host finishes registering kit contracts; copy-pasting a KitRef id that drifted from the KitDescriptor kitId; hot-reload/dev flows that re-create the host but keep stale extension code; splitting kit registration into another module that is imported lazily.
Related errors
- Kit ` ` is not available for runtime ` `.
- Module ` ` was not found.
- Tamagotchi extension tool not found
- Cannot declare permissions for unknown plugin
- Cannot grant permissions to unknown plugin
AI-assisted analysis of moeru-ai/airi@438a067dde (2026-08-18).
Data as JSON: /api/errors/98f7f14d10eecfcb.
Report an issue: GitHub.
Appendix: source
Thrown at packages/plugin-sdk/src/plugin-host/core.ts:592
}
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
}
listSessions() {
return this.extensionSessionService.list()
}
getSession(sessionId: string) {
return this.extensionSessionService.get(sessionId)
}
registerKit(kit: KitDescriptor) {View on GitHub (pinned to 438a067dde)