{"record":{"id":"f48b5aecc2dd19d1","repo":"HeyPuter/puter","slug":"forbidden-f48b5a","errorCode":"forbidden","errorMessage":"Permission denied for ${ifaceName}:${method}","messagePattern":"Permission denied for (.+?):(.+?)","errorType":"http","errorClass":"HttpError","httpStatus":403,"severity":"error","filePath":"src/backend/controllers/drivers/DriverController.ts","lineNumber":317,"sourceCode":"            const permService = this.services.permission as unknown as\n                PermissionService | undefined;\n            if (permService) {\n                // Build via PermissionUtil.join so any `:` in a driver or\n                // interface name is escaped — raw interpolation would let a\n                // crafted name shift permission-segment boundaries and match\n                // a broader/narrower parent than intended in the scan logic.\n                const permKey = PermissionUtil.join(\n                    'service',\n                    String(resolvedDriverName),\n                    'ii',\n                    ifaceName,\n                );\n                const hasPermission = await permService.check(\n                    req.actor,\n                    permKey,\n                );\n                if (!hasPermission) {\n                    throw new HttpError(\n                        403,\n                        `Permission denied for ${ifaceName}:${method}`,\n                        {\n                            legacyCode: 'forbidden',\n                        },\n                    );\n                }\n            }\n        }\n\n        // Per-method rate-limit and concurrent specs both live on the\n        // driver's resolved meta (set by `@Driver({ rateLimit, concurrent })`\n        // or imperative fields). Rate-limit is single-shot; concurrent\n        // acquires a slot that must be released when the response is done\n        // — we hook `res.finish` / `res.close` for that so streamed\n        // responses hold their slot until the stream drains, and aborted\n        // requests still give the slot back.\n        const rateLimitSpec = resolveDriverMethodRateLimit(","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/controllers/drivers/DriverController.ts#L299-L335","documentation":"Thrown by POST /drivers/call (403 forbidden) when permService.check(actor, 'service:<resolvedDriverName>:ii:<ifaceName>') returns false. The driver/method exist and are callable, but the actor lacks the per-driver service permission. This is the main authorization gate for driver RPC.","triggerScenarios":"A user/token calling a driver method without having been granted the matching 'service:<driver>:ii:<interface>' permission; calling a gated AI/external driver via a limited-scope token; a permission record that was revoked or never provisioned.","commonSituations":"App token missing the service permission for an AI driver; new driver deployed without granting the permission to existing users; permission key built with a different driver name than resolvedDriverName (e.g. alias mismatch).","solutions":["Grant the permission 'service:<driver>:ii:<interface>' to the actor/user, matching the driver's resolved driverName (instance field, then prototype meta, then requested name).","Use a full user access token (not an app-scoped one) when calling gated service drivers, if the user is the owner.","Confirm the resolvedDriverName used in the key matches what the driver declares — aliases cause silent key mismatches."],"exampleFix":"// no client code fix — this is a permission grant\nawait permService.grant(user, 'service:puter-chat-completion:ii:puter-chat-completion');","handlingStrategy":"validation","validationCode":"// server-side: confirm permission key matches resolvedDriverName\nconst has = await permService.check(actor, `service:${driverName}:ii:${ifaceName}`);\nif (!has) throw new ForbiddenError('needs service permission');","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Grant 'service:<driver>:ii:<interface>' matching resolvedDriverName.","Use a full user token for gated service drivers.","Watch for alias mismatches between driver name and permission key."],"tags":["authorization","drivers","forbidden","permissions","rpc"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}