moeru-ai/airi · error · PermissionDeniedError

Permission denied: ${details.area}.${details.action} "${deta

Error message

Permission denied: ${details.area}.${details.action} "${details.key}"

What it means

Thrown as PermissionDeniedError by assertExtensionPermission() when neither the session/module permission grant allows the requested area.action.key triple. The error carries structured `details` (area, action, key) so callers can inspect which permission was missing. It is the host's enforcement point for apis/resources/capabilities/processors/pipelines access.

Source

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

  private createModuleKitRegistry(session: ExtensionSession, subscriptions: DisposableStore, moduleId: string): ExtensionModuleContext['kits'] {
    return this.createKitRegistry(session, subscriptions, moduleId)
  }

  private assertExtensionPermission(
    session: ExtensionSession,
    input: ExtensionHostPermissionRequest,
    moduleId?: string,
  ) {
    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),

View on GitHub (pinned to 27111382b4)

Solutions

  1. Add the missing permission to the extension manifest under the correct area (apis/resources/capabilities) with the action and key shown in the error details.
  2. If the error is module-scoped, widen the input.permissions passed to ctx.modules.register to include the required key.
  3. Ensure permissionResolver (if configured) returns a grant that includes the needed entry, or remove the resolver so the manifest grant is used directly.
  4. Catch PermissionDeniedError by name to present a user-facing permission prompt instead of crashing.

Example fix

// before — manifest lacks the binding announce permission
{ id: 'my-ext', permissions: { apis: [{ key: 'plugin:binding:activate', actions: ['invoke'] }] } }

// after
{ id: 'my-ext', permissions: {
  apis: [
    { key: 'plugin:binding:announce', actions: ['invoke'] },
    { key: 'plugin:binding:activate', actions: ['invoke'] },
  ],
  resources: [{ key: 'kit:gamelet:binding', actions: ['write'] }],
} }
Defensive patterns

Strategy: try-catch

Validate before calling

// Before calling a binding API, ensure the manifest/module grant includes the required permission.
function grantAllows(grant, area, action, key): boolean { /* inspect grant entries */ }
if (!grantAllows(session.permissions.granted, 'apis', 'invoke', 'plugin:binding:announce')) {
  throw new Error('Missing required permission: apis.invoke.plugin:binding:announce')
}

Type guard

function isPermissionDeniedError(error: unknown): error is Error & { details: { area: string, action: string, key: string } } {
  return error instanceof Error && error.name === 'PermissionDeniedError'
}

Try / catch

try {
  host.announceBinding(sessionId, input)
} catch (error) {
  if (error instanceof Error && error.name === 'PermissionDeniedError') {
    // prompt the user to grant the missing permission, then retry
    const { area, action, key } = (error as any).details
    await requestPermissionFromUser({ area, action, key })
  } else {
    throw error
  }
}

Prevention

When it happens

Trigger: An extension (or module) calls a host API that triggers assertExtensionPermission — e.g. announceBinding, activateBinding, updateBinding, withdrawBinding, bindExtensionKitModule — and the manifest/module permission grant does not include the required apis.invoke.<eventName> or resources.write.<kitBindingResourceKey> entry.

Common situations: Manifest permissions block was omitted or did not declare the needed api invoke key or resource write key. A module was registered with a narrower permission grant (via input.permissions intersected with the session grant) and tried an action outside that scope. permissionResolver returned a grant missing the required entry.

Related errors


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