{"record":{"id":"fbaf9f3572ed6c2f","repo":"paperclipai/paperclip","slug":"trustpreset-detail-fbaf9f","errorCode":null,"errorMessage":"trustPreset.detail","messagePattern":"trustPreset\\.detail","errorType":"http","errorClass":null,"httpStatus":403,"severity":"error","filePath":"server/src/routes/agents.ts","lineNumber":3789,"sourceCode":"    }\n    if (\n      req.actor.keyScope?.kind === \"task_bridge\"\n      || req.actor.keyScope?.kind === \"skill_test\"\n    ) {\n      res.json({\n        id: agent.id,\n        companyId: agent.companyId,\n        name: agent.name,\n        role: agent.role,\n        title: agent.title,\n        status: agent.status,\n        keyScope: req.actor.keyScope,\n      });\n      return;\n    }\n    const trustPreset = await resolveAgentSelfTrustPreset(req, agent);\n    if (trustPreset.kind === \"denied\") {\n      res.status(403).json({ error: trustPreset.detail });\n      return;\n    }\n    if (trustPreset.kind === \"low_trust_review\") {\n      res.json(buildLowTrustSelfView(agent));\n      return;\n    }\n    res.json(await buildAgentDetail(agent));\n  });\n\n  router.get(\"/agents/me/inbox-lite\", async (req, res) => {\n    if (req.actor.type !== \"agent\" || !req.actor.agentId || !req.actor.companyId) {\n      res.status(401).json({ error: \"Agent authentication required\" });\n      return;\n    }\n\n    const issuesSvc = issueService(db);\n    const recoveryActionsSvc = issueRecoveryActionService(db);\n    const rows = await issuesSvc.list(req.actor.companyId, {","sourceCodeStart":3771,"sourceCodeEnd":3807,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/routes/agents.ts#L3771-L3807","documentation":"On GET /api/agents/me, after the special key-scope branches, the route resolves the caller's self-view trust preset via `resolveAgentSelfTrustPreset`. When the preset resolves to kind \"denied\", the route returns HTTP 403 with the preset's `detail` string as the error message. This means the agent actor's trust level does not permit a full self-detail view; the trust preset itself decides both the denial and the human-readable reason sent to the client.","triggerScenarios":"An authenticated agent (bearer key, non task_bridge/skill_test scope) calls GET /agents/me while resolveAgentSelfTrustPreset returns { kind: \"denied\", detail } — e.g., the agent's trust preset configuration disallows self-detail reads for its current trust tier/key scope.","commonSituations":"An agent with a restricted trust preset calling the full self endpoint instead of the low-trust view; trust preset config changed server-side (or via env/feature flag) after the key was issued; calling /agents/me with a key whose trust tier was downgraded; CLI/adapter SDK still using an endpoint the agent's preset no longer allows.","solutions":["Read the `detail` message in the 403 body — it names the specific trust-preset reason; act on that.","Have a board operator review/raise the agent's trust preset configuration if full self-detail access is intended.","Fall back to the low-trust self view (buildLowTrustSelfView surface) or the limited key-scope branch data the agent is entitled to.","Re-issue the agent key with the correct scope/trust tier if the key's scope no longer matches its intended use.","Check recent config/feature-flag changes to trust presets around the time the error started."],"exampleFix":"// before: client assumes /agents/me always returns full detail\nconst me = await api.get('/agents/me');\n// after: handle the denied/low-trust variants\nconst res = await fetch('/agents/me', { headers: auth });\nif (res.status === 403) return useLowTrustSelfView(); // render limited view from body.detail\nconst me = await res.json();","handlingStrategy":"fallback","validationCode":"// probe the self endpoint and degrade gracefully\nconst res = await fetch('/api/agents/me', { headers: agentAuth });\nif (res.status === 403) {\n  const { error } = await res.json();\n  console.warn('self detail denied by trust preset:', error);\n}","typeGuard":"type TrustPresetDecision = { kind: 'allowed' } | { kind: 'low_trust_review' } | { kind: 'denied'; detail: string };\nfunction isDenied(p: TrustPresetDecision): p is { kind: 'denied'; detail: string } {\n  return p.kind === 'denied';\n}","tryCatchPattern":"try {\n  return await api.getAgentSelfDetail();\n} catch (err) {\n  if (err.status === 403) return buildLowTrustSelfViewFromError(err); // limited view\n  throw err;\n}","preventionTips":["Keep client code ready to render the low-trust/limited self view.","Track trust preset configuration changes alongside key issuance so keys match their tier.","When issuing agent keys, record the intended trust tier and assert endpoints against it.","Surface body.detail from 403s in logs instead of discarding it."],"tags":["authorization","trust-preset","http-403","agent-auth"],"backgroundTag":"insufficient-permissions","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}