moeru-ai/airi · error · Error
Cannot declare permissions for unknown plugin
Error message
Cannot declare permissions for unknown plugin "${extensionId}". What it means
Thrown by PermissionService.declare when store.get(extensionId) is unset. The in-memory permission store is populated only by initialize(extensionId, declaration, options) during session setup; declare is strictly an incremental merge on top of an initialized snapshot, so calling it for a plugin the service has never initialized is a state-machine violation.
Solutions
- Initialize first: permissions.initialize(extensionId, declaration) (normally done by host.start) before any declare call.
- Ensure the exact same extensionId is used for initialize and declare — compare against session.extension.id.
- In tests/devtools, bootstrap via the host (start the extension) instead of hand-calling declare on a bare service.
- Guard the call: if (!permissions.get(extensionId)) initialize, then declare.
Example fix
// before
svc.declare('ext-1', { apis: [...] }) // fresh PermissionService -> throws
// after
svc.initialize('ext-1', { apis: [...] })
svc.declare('ext-1', { resources: [...] }) Defensive patterns
Strategy: validation
Validate before calling
// Before declare:
if (!permissions.get(extensionId)) {
permissions.initialize(extensionId, requestedDeclaration)
return
}
permissions.declare(extensionId, requestedDeclaration) Type guard
const isPermissionStateInitialized = (svc: PermissionService, extensionId: string): boolean => svc.get(extensionId) !== undefined
Try / catch
try {
svc.declare(extensionId, declaration)
} catch (error) {
if (error instanceof Error && error.message.includes('unknown plugin')) {
svc.initialize(extensionId, declaration) // first-time declaration doubles as init
return
}
throw error
} Prevention
- Drive all permission mutations through the host session lifecycle (start initializes) instead of calling the service directly.
- In devtools/tests, check svc.get(extensionId) before enabling declare/grant actions.
- Use one canonical extensionId (session.extension.id) everywhere.
When it happens
Trigger: Invoking permissions.declare('ext-1', ...) before the host started that extension (initialize runs during start); using a different extensionId string than the one passed to initialize (casing, versioned id); calling declare on a freshly constructed PermissionService/Host in tests without bootstrapping; declaring after the host was recreated.
Common situations: Test helpers that exercise declare/grant directly; devtools that mutate permissions before the target extension session exists; extensionId drift between manifest, session registration, and permission tooling; multiple host instances sharing nothing in-memory.
Related errors
- Cannot grant permissions to unknown plugin
- Permission denied: .
- 1. Microphone permission not granted - browser should…
- AudioContext not initialized
- AudioContext or worklets not ready
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/1b7d8525b02e11ca.
Report an issue: GitHub.
Appendix: source
Thrown at packages/plugin-sdk/src/plugin-host/runtimes/shared/services/permissions.ts:322
const persisted = options?.persisted ?? {}
const explicitGrant = options?.grant ?? requested
const mergedGrant = mergePermissions(persisted, explicitGrant)
const granted = intersectPermissions(requested, mergedGrant)
const previousRevision = this.store.get(extensionId)?.revision ?? 0
const snapshot: PermissionSnapshot = {
requested,
granted,
revision: previousRevision + 1,
}
this.store.set(extensionId, snapshot)
return snapshot
}
declare(extensionId: string, requestedDeclaration: ModulePermissionDeclaration) {
const existing = this.store.get(extensionId)
if (!existing) {
throw new Error(`Cannot declare permissions for unknown plugin "${extensionId}".`)
}
const requested = normalizeDeclaration(requestedDeclaration)
const snapshot: PermissionSnapshot = {
requested: mergePermissionDeclarations(existing.requested, requested),
granted: existing.granted,
revision: existing.revision + 1,
}
this.store.set(extensionId, snapshot)
return snapshot
}
grant(extensionId: string, grant: ModulePermissionGrant) {
const existing = this.store.get(extensionId)
if (!existing) {
throw new Error(`Cannot grant permissions to unknown plugin "${extensionId}".`)
}View on GitHub (pinned to 677329427f)