{"record":{"id":"98f7f14d10eecfcb","repo":"moeru-ai/airi","slug":"kit-kitid-is-not-registered","errorCode":null,"errorMessage":"Kit `${kitId}` is not registered.","messagePattern":"Kit `(.+?)` is not registered\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/plugin-sdk/src/plugin-host/core.ts","lineNumber":592,"sourceCode":"    }\n    await this.cleanupExtensionSessionModules(session)\n    await session.subscriptions.dispose()\n    this.extensionSessionService.remove(session.id)\n  }\n\n  private getModuleOrThrow(moduleId: string) {\n    const module = this.modules.get(moduleId)\n    if (!module) {\n      throw new Error(`Module \\`${moduleId}\\` was not found.`)\n    }\n\n    return module\n  }\n\n  private assertKitAvailableForRuntime(kitId: string, runtime: PluginRuntime) {\n    const kit = this.kits.get(kitId)\n    if (!kit) {\n      throw new Error(`Kit \\`${kitId}\\` is not registered.`)\n    }\n\n    if (!kit.runtimes.includes(runtime)) {\n      throw new Error(`Kit \\`${kitId}\\` is not available for runtime \\`${runtime}\\`.`)\n    }\n\n    return kit\n  }\n\n  listSessions() {\n    return this.extensionSessionService.list()\n  }\n\n  getSession(sessionId: string) {\n    return this.extensionSessionService.get(sessionId)\n  }\n\n  registerKit(kit: KitDescriptor) {","sourceCodeStart":574,"sourceCodeEnd":610,"githubUrl":"https://github.com/moeru-ai/airi/blob/438a067dde47aa0bdb46c2323d1fe293dc805218/packages/plugin-sdk/src/plugin-host/core.ts#L574-L610","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\nconst session = await host.start(manifest) // extension setup calls announceBinding('my-kit')\nhost.registerKit({ kitId: 'my-kit', runtimes: ['node'], capabilities: [] }) // too late\n\n// after\nhost.registerKit({ kitId: 'my-kit', runtimes: ['node'], capabilities: [] })\nconst session = await host.start(manifest) // kit already registered when the extension announces","handlingStrategy":"validation","validationCode":"// Before announceBinding / starting the extension:\nconst kit = host.getKit('my-kit')\nif (!kit) {\n  host.registerKit({ kitId: 'my-kit', runtimes: ['node'], capabilities: [] })\n}\n// Optionally sanity-check the full registry:\nconst registered = host.listKits().map(k => k.kitId)\nif (!registered.includes('my-kit')) throw new Error(`Register kits first. Have: ${registered.join(', ')}`)","typeGuard":"const isKitRegistered = (host: ExtensionHost, kitId: string): boolean => host.getKit(kitId) !== undefined","tryCatchPattern":"try {\n  host.announceBinding(sessionId, { kitId: 'my-kit', moduleId: 'm-1', ... })\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('Kit `') && error.message.includes('is not registered.')) {\n    // register the kit, then retry the announce once\n  }\n  throw error\n}","preventionTips":["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."],"tags":["plugin-sdk","kit","registry","initialization-order"],"backgroundTag":"service-not-registered","analyzedSha":"438a067dde47aa0bdb46c2323d1fe293dc805218","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}