moeru-ai/airi · error · Error

Unknown extension session

Error message

Unknown extension session: ${sessionId}

What it means

getExtensionSessionOrThrow (core.ts) maps a sessionId to a live ExtensionSession via the extension session service and throws this error when the id is unknown. Sessions are created by startExtension and removed when the extension stops (stopExtension disposes subscriptions and removes the session), so this error means the caller referenced a session that never existed or that has already been torn down. Most host APIs that operate per-session resolve through this lookup first.

Solutions

  1. Re-acquire the session: call startExtension again (or re-read the active sessions list) and use the fresh session id.
  2. Treat this error as a signal to stop issuing calls with the stale handle — clear cached ids when an extension is stopped/disabled.
  3. Serialize stop-then-call sequences so host calls complete before stopExtension removes the session.
  4. Never persist session ids; they are per-run identities by design (session ids come from nextSessionIdentity).

Example fix

// before
const session = await host.startExtension(ext, { manifest })
// ... later, after host.stopExtension(session.id)
await host.useModule(oldSessionId, moduleId) // throws: session removed

// after
await host.stopExtension(session.id)
const fresh = await host.startExtension(ext, { manifest })
await host.useModule(fresh.id, moduleId)
Defensive patterns

Strategy: validation

Validate before calling

// before using a cached sessionId, confirm the session is still live
const sessions = host.listSessions?.() ?? []
if (!sessions.some(s => s.id === cachedSessionId)) {
  cachedSession = await host.startExtension(extension, { manifest }) // re-establish
}

Try / catch

try {
  await host.withSession(sessionId, op)
} catch (error) {
  if (errorMessageFrom(error).startsWith('Unknown extension session:')) {
    // extension was stopped/reloaded: drop the cached handle and re-acquire
    sessionId = undefined
    return
  }
  throw error
}

Prevention

When it happens

Trigger: Using a sessionId captured before a stopExtension/dispose (e.g. after hot-reload, extension disable, or app teardown); a typo or truncated id; concurrent stop racing an in-flight host call that carries the old session id; persisting session ids across app restarts and replaying them.

Common situations: Dev hot-reload restarting extensions while UI code holds old session handles; disable/re-enable flows where pending promises resolve after removal; distributed/multi-window callers caching session ids longer than their lifetime.

Related errors


AI-assisted analysis of moeru-ai/airi@438a067dde (2026-08-18). Data as JSON: /api/errors/46b847f9a422a871. Report an issue: GitHub.

Appendix: source

Thrown at packages/plugin-sdk/src/plugin-host/core.ts:537

    const grant = moduleId
      ? session.modules.get(moduleId)?.permissions
      : session.permissions.granted

    if (grant && this.permissions.grantAllows(grant, input.area, input.action, input.key)) {
      return
    }

    throw new PermissionDeniedError({
      area: input.area,
      action: input.action,
      key: input.key,
    })
  }

  private getExtensionSessionOrThrow(sessionId: string) {
    const session = this.extensionSessionService.get(sessionId)
    if (!session) {
      throw new Error(`Unknown extension session: ${sessionId}`)
    }

    return session
  }

  private createInstallContext(): ExtensionHostInstallContext {
    return {
      registerKit: kit => this.registerKit(kit),
      unregisterKit: kitId => this.unregisterKit(kitId),
      setResourceResolver: (key, resolver) => this.setResourceResolver(key, resolver),
      setResourceValue: (key, value) => this.setResourceValue(key, value),
      announceCapability: (key, metadata) => {
        this.announceCapability(key, metadata)
      },
      markCapabilityReady: (key, metadata) => {
        this.markCapabilityReady(key, metadata)
      },
      markCapabilityDegraded: (key, metadata) => {

View on GitHub (pinned to 438a067dde)