{"record":{"id":"75659e6ac0109ec6","repo":"HeyPuter/puter","slug":"token-auth-failed","errorCode":"token_auth_failed","errorMessage":"Token authentication failed","messagePattern":"Token authentication failed","errorType":"http","errorClass":"HttpError","httpStatus":401,"severity":"error","filePath":"src/backend/controllers/fs/LegacyFSController.ts","lineNumber":1207,"sourceCode":"        if (download.lastModified)\n            res.setHeader('Last-Modified', download.lastModified.toUTCString());\n        res.setHeader(\n            'Content-Disposition',\n            `inline; filename=\"${encodeURIComponent(entry.name)}\"`,\n        );\n        res.status(range ? 206 : 200);\n\n        download.body.on('error', (err) => {\n            res.destroy(err);\n        });\n        download.body.pipe(res);\n    };\n\n    tokenRead = async (req: Request, res: Response): Promise<void> => {\n        const query = asRecord(req.query);\n        const accessToken = getString(query, 'token');\n        if (!accessToken) {\n            throw new HttpError(401, 'Token authentication failed', {\n                legacyCode: 'token_auth_failed',\n            });\n        }\n\n        const actor =\n            await this.services.auth.authenticateFromToken(accessToken);\n        if (!isAccessTokenActor(actor)) {\n            throw new HttpError(401, 'Token authentication failed', {\n                legacyCode: 'token_auth_failed',\n            });\n        }\n\n        // This endpoint authenticates the token by hand and never runs the\n        // route gate chain, so the suspension and pending-verification checks\n        // that guard every other authenticated FS route have to run here.\n        assertNotSuspended(actor!.user);\n        assertVerifiedAccount(actor!.user);\n","sourceCodeStart":1189,"sourceCodeEnd":1225,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/controllers/fs/LegacyFSController.ts#L1189-L1225","documentation":"Thrown by tokenRead (GET /read with ?token=) when the request has no `token` query parameter at all. This endpoint authenticates a token by hand instead of going through the normal auth gate chain, so the very first check is that a token string was supplied. Without it the request is anonymous and cannot be authorized to read any file.","triggerScenarios":"Calling GET /read?uid=<uid> (or /read with a path) while omitting the `token` query param, or passing `?token=` (empty string). getString(query,'token') returns '' and the truthiness check fails.","commonSituations":"A puter.js readWithToken/signed-read flow that forgot to append the token; a bookmarked /read URL whose token query got stripped by a redirect or proxy; building the URL manually and missing the key.","solutions":["Append the access token as a query parameter: GET /read?token=<your_access_token>&uid=<uid>.","If you obtained a signed URL from /sign or /open_item, use the returned read_url/token field verbatim rather than reconstructing it.","Confirm the token is not being dropped by URL encoding or a redirecting gateway that strips unknown params."],"exampleFix":"// before\nfetch('/read?uid=' + uid)\n// after\nfetch('/read?uid=' + uid + '&token=' + encodeURIComponent(accessToken))","handlingStrategy":"validation","validationCode":"// client-side guard before building the /read?token= URL\nfunction buildTokenReadUrl(uid, token) {\n  if (typeof token !== 'string' || token.trim().length === 0) {\n    throw new Error('Cannot call /read without an access token');\n  }\n  const params = new URLSearchParams({ uid, token: token.trim() });\n  return `/read?${params.toString()}`;\n}","typeGuard":"// narrow a candidate token to a non-empty string\n/** @param {unknown} t\n * @returns {t is string}\n */\nfunction isNonEmptyToken(t) {\n  return typeof t === 'string' && t.trim().length > 0;\n}","tryCatchPattern":"try {\n  const res = await fetch(buildTokenReadUrl(uid, token));\n  if (res.status === 401) { /* re-auth, then retry once */ }\n} catch (e) { /* network error, not the 401 */ }","preventionTips":["Always source the token from the auth module rather than hand-building it.","Strip whitespace before sending to avoid empty-after-trim rejections.","Treat a 401 token_auth_failed as a signal to refresh the token, not to retry unchanged."],"tags":["auth","query-param","token","fs-read"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}