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
- 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.
- If the error is module-scoped, widen the input.permissions passed to ctx.modules.register to include the required key.
- Ensure permissionResolver (if configured) returns a grant that includes the needed entry, or remove the resolver so the manifest grant is used directly.
- 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
- Declare every api/resource/capability key the extension will use in the manifest permissions block.
- When narrowing module permissions via input.permissions, include all keys the module will exercise.
- If using a permissionResolver, ensure it returns a grant covering the declared keys.
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
- Cannot declare permissions for unknown plugin "${extensionId
- Cannot grant permissions to unknown plugin "${extensionId}".
- Selected source did not provide a live video track
- botCall: method "${method}" is not allowed
- Unknown extension session: ${sessionId}
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/9b638c155bccd3b7.
Report an issue: GitHub.