moeru-ai/airi · error · Error

Module `${moduleId}` was not found.

Error message

Module `${moduleId}` was not found.

What it means

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.

Source

Thrown at packages/plugin-sdk/src/plugin-host/runtimes/shared/services/kit-api-bindings.ts:419

   * Use when:
   * - A caller needs a custom lifecycle transition beyond the convenience helpers
   *
   * Expects:
   * - `owner` matches the stored binding owner
   * - `state`, when provided, is legal from the current lifecycle state
   *
   * Returns:
   * - The next canonical binding record written back into the registry
   */
  transition(
    owner: BindingOwnerIdentity,
    moduleId: string,
    state?: BindingState,
    patch: BindingUpdatePatch<C> = {},
  ) {
    const current = this.bindings.get(moduleId)
    if (!current) {
      throw new Error(`Module \`${moduleId}\` was not found.`)
    }

    if (
      current.ownerSessionId !== owner.ownerSessionId
      || current.ownerExtensionId !== owner.ownerExtensionId
    ) {
      throw createOwnershipError(
        moduleId,
        {
          ownerSessionId: current.ownerSessionId,
          ownerExtensionId: current.ownerExtensionId,
        },
        owner,
      )
    }

    const nextState = state ?? current.state
    if (!allowedBindingTransitions[current.state].includes(nextState)) {

View on GitHub (pinned to 27111382b4)

Solutions

  1. Ensure the module is registered with the registry (bind/init) before calling transition/withdraw — check that the same `moduleId` was used at registration time.
  2. 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.
  3. If this fires during shutdown/teardown paths, treat a missing binding as already-cleaned-up and make the caller idempotent rather than throwing.
  4. Audit for stale module-id references after a hot-reload or session restart that resets the registry.

Example fix

// before
registry.withdraw(sessionId, extensionId, moduleId)

// after
if (registry.get(moduleId)) {
  registry.withdraw(sessionId, extensionId, moduleId)
}
Defensive patterns

Strategy: validation

Validate before calling

if (!registry.get(moduleId)) {
  // skip transition or initialize the binding first
  return
}
registry.transition(owner, moduleId, state, patch)

Type guard

function isBound(moduleId: string): boolean {
  return registry.get(moduleId) !== undefined
}

Try / catch

try {
  registry.transition(owner, moduleId, state, patch)
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Module `') && e.message.endsWith('` was not found.')) {
    // binding already removed during teardown — treat as success
    return
  }
  throw e
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/b0fd6b5e5d3a38f1. Report an issue: GitHub.