moeru-ai/airi · error · Error

Unknown extension session: ${sessionId}

Error message

Unknown extension session: ${sessionId}

What it means

Thrown by the private getExtensionSessionOrThrow() when no ExtensionSession exists for the given sessionId. It is the precondition check for host methods that operate on a live session: announceBinding, activateBinding, updateBinding, degradeBinding, withdrawBinding, bindExtensionKitModule. The session may have been stopped, never started, or the id may be wrong.

Source

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

    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 27111382b4)

Solutions

  1. Use the sessionId returned by host.start() and avoid caching it across stop()/reload() calls.
  2. After host.stop() or reload(), refresh the sessionId from host.getSession() or listSessions() before calling binding methods.
  3. Check host.getSession(sessionId) returns a defined session before invoking binding APIs.

Example fix

// before — using a stale id after stop
const session = await host.start(manifest)
await host.stop(session.id)
await host.announceBinding(session.id, input) // throws Unknown extension session

// after
const current = host.getSession(session.id)
if (!current) throw new Error('Session no longer active')
await host.announceBinding(current.id, input)
Defensive patterns

Strategy: validation

Validate before calling

const session = host.getSession(sessionId)
if (!session) {
  throw new Error(`Session '${sessionId}' is not active. Call host.start(manifest) first.`)
}
host.announceBinding(sessionId, input)

Type guard

function isSessionActive(host: ExtensionHost, sessionId: string): boolean {
  return host.getSession(sessionId) !== undefined
}

Try / catch

try {
  return host.announceBinding(sessionId, input)
} catch (error) {
  if (error instanceof Error && /Unknown extension session/.test(error.message)) {
    // session was stopped or never started; refresh the session id
  } else {
    throw error
  }
}

Prevention

When it happens

Trigger: Calling any session-scoped ExtensionHost binding method (announceBinding, activateBinding, updateBinding, degradeBinding, withdrawBinding, bindExtensionKitModule) with a sessionId that was never started, was already stopped via host.stop(), or is a typo.

Common situations: Holding a stale sessionId after the extension was stopped or reloaded (reload creates a new session identity). Passing a sessionId from a different host instance. Race where the session is torn down between obtaining the id and calling a binding method.

Related errors


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