{"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/27111382b4a79a7e983289d6e983a06af185ed0f/packages/plugin-sdk/src/plugin-host/runtimes/shared/services/kit-api-bindings.ts#L401-L437","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nregistry.withdraw(sessionId, extensionId, moduleId)\n\n// after\nif (registry.get(moduleId)) {\n  registry.withdraw(sessionId, extensionId, moduleId)\n}","handlingStrategy":"validation","validationCode":"if (!registry.get(moduleId)) {\n  // skip transition or initialize the binding first\n  return\n}\nregistry.transition(owner, moduleId, state, patch)","typeGuard":"function isBound(moduleId: string): boolean {\n  return registry.get(moduleId) !== undefined\n}","tryCatchPattern":"try {\n  registry.transition(owner, moduleId, state, patch)\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Module `') && e.message.endsWith('` was not found.')) {\n    // binding already removed during teardown — treat as success\n    return\n  }\n  throw e\n}","preventionTips":["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."],"tags":["plugin-sdk","bindings","lifecycle","state-machine"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}