{"record":{"id":"b0be6f53ca4cc79e","repo":"HeyPuter/puter","slug":"access-denied","errorCode":"access_denied","errorMessage":"Access denied","messagePattern":"Access denied","errorType":"exception","errorClass":"HttpError","httpStatus":404,"severity":"error","filePath":"src/backend/controllers/fs/FSController.ts","lineNumber":1747,"sourceCode":"            descriptor,\n            mode,\n        )) as {\n            status?: unknown;\n            message?: unknown;\n            fields?: { code?: unknown };\n        };\n        const status = Number(safe?.status);\n        const message =\n            typeof safe?.message === 'string' && safe.message.length > 0\n                ? safe.message\n                : 'Access denied';\n        const code =\n            typeof safe?.fields?.code === 'string'\n                ? safe.fields.code\n                : undefined;\n        const legacyCode = code === 'forbidden' ? 'access_denied' : code;\n        if (status === 404) {\n            throw new HttpError(404, message, {\n                ...(legacyCode ? { legacyCode } : {}),\n            });\n        }\n        throw new HttpError(403, message, {\n            legacyCode: legacyCode ?? 'access_denied',\n        });\n    }\n\n    #toNumberOrUndefined(value: unknown): number | undefined {\n        if (typeof value === 'number' && Number.isFinite(value)) return value;\n        if (typeof value === 'string' && value.trim().length > 0) {\n            const parsed = Number(value);\n            if (Number.isFinite(parsed)) return parsed;\n        }\n        return undefined;\n    }\n\n    // Accepts loose inputs from route bodies. `source`/`destination` fields may","sourceCodeStart":1729,"sourceCodeEnd":1765,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/controllers/fs/FSController.ts#L1729-L1765","documentation":"Thrown by FSController.#assertAccess when the ACL check denies access and `acl.getSafeAclError` reports a 404 status. The 404 path means the actor is treated as unable to even *see* the resource — the system declines to confirm the entry exists to that actor. This is the standard 'deny hides existence' behavior layered on top of ACL evaluation.","triggerScenarios":"Calling an FS read/write operation on a path the actor has no `see`/`list` permission on, where the ACL service returns a not-found-style safe error. Common with private folders under another user's home, or paths inside an app's AppData owned by a different app uid.","commonSituations":"App attempting to read outside its AppData scope; user navigating to a path they can't see; deleted-from-share target; an ACL cache that hasn't propagated a newly-granted permission.","solutions":["Confirm the path is shared with or owned by the actor before issuing the call.","Re-issue with an explicit uid of an entry the actor can see, or request access from the owner.","Treat the 404 as authoritative in the client — do not assume a later retry will succeed without a permission change.","Audit ACLService rules / shares for the path if access was expected."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await op(); }\ncatch (e) {\n  if (e?.status === 404 && /denied|access/i.test(e.message)) {\n    // entry not visible to actor; do not disclose existence to end user\n  } else throw e;\n}","preventionTips":["Check share/permission UI before issuing cross-user reads.","Don't leak 404-vs-403 distinctions to end users; both mean 'unavailable'.","Audit ACL rules when expected access returns 404-from-ACL."],"tags":["fs","acl","access-denied","api","not-found"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}