{"record":{"id":"1b7d8525b02e11ca","repo":"moeru-ai/airi","slug":"cannot-declare-permissions-for-unknown-plugin-e","errorCode":null,"errorMessage":"Cannot declare permissions for unknown plugin \"${extensionId}\".","messagePattern":"Cannot declare permissions for unknown plugin \"(.+?)\"\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/plugin-sdk/src/plugin-host/runtimes/shared/services/permissions.ts","lineNumber":322,"sourceCode":"    const persisted = options?.persisted ?? {}\n    const explicitGrant = options?.grant ?? requested\n    const mergedGrant = mergePermissions(persisted, explicitGrant)\n    const granted = intersectPermissions(requested, mergedGrant)\n    const previousRevision = this.store.get(extensionId)?.revision ?? 0\n    const snapshot: PermissionSnapshot = {\n      requested,\n      granted,\n      revision: previousRevision + 1,\n    }\n\n    this.store.set(extensionId, snapshot)\n    return snapshot\n  }\n\n  declare(extensionId: string, requestedDeclaration: ModulePermissionDeclaration) {\n    const existing = this.store.get(extensionId)\n    if (!existing) {\n      throw new Error(`Cannot declare permissions for unknown plugin \"${extensionId}\".`)\n    }\n\n    const requested = normalizeDeclaration(requestedDeclaration)\n    const snapshot: PermissionSnapshot = {\n      requested: mergePermissionDeclarations(existing.requested, requested),\n      granted: existing.granted,\n      revision: existing.revision + 1,\n    }\n\n    this.store.set(extensionId, snapshot)\n    return snapshot\n  }\n\n  grant(extensionId: string, grant: ModulePermissionGrant) {\n    const existing = this.store.get(extensionId)\n    if (!existing) {\n      throw new Error(`Cannot grant permissions to unknown plugin \"${extensionId}\".`)\n    }","sourceCodeStart":304,"sourceCodeEnd":340,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/packages/plugin-sdk/src/plugin-host/runtimes/shared/services/permissions.ts#L304-L340","documentation":"Thrown by `PermissionRegistry.declare()` when no permission snapshot exists in the store for the given `extensionId`. `declare()` is meant to amend an already-initialized plugin's requested permissions; it requires `initialize()` to have been called first for that extension id. The store lookup is the first operation, so any unknown extension id fails here before declaration merging.","triggerScenarios":"Calling `permissions.declare(extensionId, declaration)` for an extension that has not yet been registered via `permissions.initialize(extensionId, ...)`. Also triggered after a registry reset/clear, by a typo in the extension id, or by out-of-order host wiring that declares permissions before the extension registration step completes.","commonSituations":"Plugin host bootstrap that calls `declare()` before `initialize()`; extension ids that differ between registration and declaration (e.g. name vs. scoped id); reloading a plugin into a fresh permission store without re-initializing; tests that exercise declare without seeding the store.","solutions":["Call `permissions.initialize(extensionId, requestedDeclaration)` once before any `declare()` or `grant()` for that extension.","Verify the `extensionId` argument exactly matches the id used at initialization (same casing, scope, no trailing whitespace).","Guard with `permissions.get(extensionId)` before calling `declare()`, and initialize on demand if missing.","If the store was reset (reload, test isolation), re-run the full initialization sequence, not just declare."],"exampleFix":"// before\npermissions.declare(extensionId, declaration)\n\n// after\nif (!permissions.get(extensionId)) {\n  permissions.initialize(extensionId, declaration)\n} else {\n  permissions.declare(extensionId, declaration)\n}","handlingStrategy":"validation","validationCode":"if (!permissions.get(extensionId)) {\n  permissions.initialize(extensionId, requestedDeclaration)\n} else {\n  permissions.declare(extensionId, requestedDeclaration)\n}","typeGuard":"function isPermissionInitialized(extensionId: string): boolean {\n  return permissions.get(extensionId) !== undefined\n}","tryCatchPattern":"try {\n  permissions.declare(extensionId, requestedDeclaration)\n} catch (e) {\n  if (e instanceof Error && e.message.includes('Cannot declare permissions for unknown plugin')) {\n    permissions.initialize(extensionId, requestedDeclaration)\n    return\n  }\n  throw e\n}","preventionTips":["Establish a strict bootstrap order: initialize every extension's permissions before any declare/grant call.","Share a single normalized extension id between registration and permission calls.","After a store reset, replay the full initialize sequence, not just incremental ops.","Guard declare/grant with permissions.get(extensionId) in code paths that may run out of order."],"tags":["plugin-sdk","permissions","lifecycle","authorization"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}