{"record":{"id":"b0fd6b5e5d3a38f1","repo":"moeru-ai/airi","slug":"module-moduleid-was-not-found-b0fd6b","errorCode":null,"errorMessage":"Module `${moduleId}` was not found.","messagePattern":"Module `(.+?)` was not found\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/plugin-sdk/src/plugin-host/runtimes/shared/services/kit-api-bindings.ts","lineNumber":419,"sourceCode":"   * Use when:\n   * - A caller needs a custom lifecycle transition beyond the convenience helpers\n   *\n   * Expects:\n   * - `owner` matches the stored binding owner\n   * - `state`, when provided, is legal from the current lifecycle state\n   *\n   * Returns:\n   * - The next canonical binding record written back into the registry\n   */\n  transition(\n    owner: BindingOwnerIdentity,\n    moduleId: string,\n    state?: BindingState,\n    patch: BindingUpdatePatch<C> = {},\n  ) {\n    const current = this.bindings.get(moduleId)\n    if (!current) {\n      throw new Error(`Module \\`${moduleId}\\` was not found.`)\n    }\n\n    if (\n      current.ownerSessionId !== owner.ownerSessionId\n      || current.ownerExtensionId !== owner.ownerExtensionId\n    ) {\n      throw createOwnershipError(\n        moduleId,\n        {\n          ownerSessionId: current.ownerSessionId,\n          ownerExtensionId: current.ownerExtensionId,\n        },\n        owner,\n      )\n    }\n\n    const nextState = state ?? current.state\n    if (!allowedBindingTransitions[current.state].includes(nextState)) {","sourceCodeStart":401,"sourceCodeEnd":437,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/plugin-sdk/src/plugin-host/runtimes/shared/services/kit-api-bindings.ts#L401-L437","documentation":"Thrown by KitApiBindingRegistry.transition (and the update/activate/degrade/withdraw helpers that delegate to it) when this.bindings.get(moduleId) returns nothing. Every lifecycle mutation requires a live binding record; the moduleId was either never announced, already unbound during session cleanup, or is a stale id held by a caller that outlived the binding.","triggerScenarios":"Calling activate/update/degrade/withdraw with a moduleId from a previous session after reload (reload creates a new session identity and re-announces new ids); transitioning after cleanupExtensionSession unbound the module; typo or wrong variable passed as moduleId; announcing and transitioning through two different registry instances.","commonSituations":"Kit helpers caching module ids across reloads; UI code reacting to a stop/reload and then trying to degrade a now-removed module; concurrent teardown and update paths racing; tests reusing ids from a prior registry.","solutions":["Check liveness first: if (!host.getBinding(moduleId)) return — skip or re-announce to obtain a fresh moduleId.","After host.reload(), re-discover ids via host.listBindings({ ownerSessionId }) instead of reusing pre-reload moduleIds.","Verify the moduleId string at the call site (log it) to rule out typos or an unset variable.","Sequence teardown before updates: stop reacting to a session's events once cleanup has begun so late transitions never fire."],"exampleFix":"// before\nregistry.activate(owner, moduleId) // moduleId from before host.reload(...) -> throws\n\n// after\nif (!host.getBinding(moduleId)) {\n  const [record] = host.listBindings({ ownerSessionId: newSession.id, kitId })\n  moduleId = record!.moduleId\n}\nregistry.activate(owner, moduleId)","handlingStrategy":"validation","validationCode":"// Before activate/degrade/withdraw/update:\nif (!host.getBinding(moduleId)) {\n  const [record] = host.listBindings({ ownerSessionId: session.id })\n  if (!record) return // binding already cleaned up; nothing to transition\n  moduleId = record.moduleId\n}","typeGuard":"const isLiveBinding = (host: ExtensionHost, moduleId: string): boolean => host.getBinding(moduleId) !== undefined","tryCatchPattern":"try {\n  registry.activate(owner, moduleId)\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('Module `') && error.message.includes('was not found.')) {\n    return // stale id after reload/cleanup — drop it instead of crashing\n  }\n  throw error\n}","preventionTips":["Refetch moduleIds from host.listBindings() after every reload; never cache them across session boundaries.","Ignore session events once teardown has started so late transitions hit no dead binding.","Log moduleId at transition call sites to catch typos early."],"tags":["plugin-sdk","bindings","lifecycle","stale-id"],"backgroundTag":"stale-handle","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}