{"record":{"id":"32c56046c55f6e39","repo":"paperclipai/paperclip","slug":"agent-is-outside-this-actor-s-authorization-bounda","errorCode":null,"errorMessage":"Agent is outside this actor's authorization boundary","messagePattern":"Agent is outside this actor's authorization boundary","errorType":"http","errorClass":null,"httpStatus":403,"severity":"error","filePath":"server/src/routes/agents.ts","lineNumber":590,"sourceCode":"    // vice versa): each entry closes over its own readiness check and its own\n    // promotion function.\n    promotionByAdapterType: {\n      codex_local: {\n        async promote(authBytes, context) {\n          // Hold the promotion critical-section lock across the ownership check and\n          // the credential write. The reaper takes the same lock before it reclaims\n          // a stale `promoting` row. So a reclaim never interleaves with a live\n          // write: the reaper either wins the lock first and the ownership check\n          // then reads a reclaimed row and writes nothing, or the write finishes\n          // first under the lock and the reaper reclaims only after it completes. A\n          // read-only fence is not enough, because the filesystem write can start\n          // after the fence; the lock spans the whole section.\n          const outcome = await adapterLoginStore.withCompanyAdapterPromotionLock(\n            context.companyId,\n            context.startedByUserId,\n            context.adapterType,\n            () =>\n              promoteDeviceLoginCredential({\n                authBytes,\n                companyId: context.companyId,\n                userInitiated: true,\n                checkReadiness: (bytes) => checkStagedCredentialReadiness(bytes),\n                isSoleActiveOwner: async () => {\n                  // The partial unique index allows one active row per company and\n                  // adapter. So a `promoting` row for this session is the sole\n                  // active owner of the company credential slot. The read runs\n                  // inside the lock, so it observes a reaper reclaim that committed\n                  // before this section acquired the lock.\n                  const row = await adapterLoginStore.get(context.sessionId);\n                  return row?.status === \"promoting\" && row.companyId === context.companyId;\n                },\n                log: (line) => {\n                  // The promotion lines carry no token bytes and no raw account id,\n                  // so it is safe to log them with the session identifier.\n                  logger.info({ sessionId: context.sessionId }, line);\n                },","sourceCodeStart":572,"sourceCodeEnd":608,"githubUrl":"https://github.com/paperclipai/paperclip/blob/5716fe907e596ce73501408fc6efdb19fb61edf2/server/src/routes/agents.ts#L572-L608","documentation":"The agents route's access check (assertAgentReadAllowed / access.decide) denied agent:read for the requesting actor: the agent lies outside the actor's authorization boundary (wrong company or insufficient grants). Surfaced as a forbidden/authorization failure.","triggerScenarios":"Thrown at server/src/routes/agents.ts:584 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Access only agents within the actor's authorized company scope."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"5716fe907e596ce73501408fc6efdb19fb61edf2","analyzedAt":"2026-08-18T22:49:45.177Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}