HeyPuter/puter · error · HttpError
token_auth_failed
token_auth_failed
Error message
Token authentication failed
What it means
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.
Source
Thrown at src/backend/controllers/fs/LegacyFSController.ts:1207
if (download.lastModified)
res.setHeader('Last-Modified', download.lastModified.toUTCString());
res.setHeader(
'Content-Disposition',
`inline; filename="${encodeURIComponent(entry.name)}"`,
);
res.status(range ? 206 : 200);
download.body.on('error', (err) => {
res.destroy(err);
});
download.body.pipe(res);
};
tokenRead = async (req: Request, res: Response): Promise<void> => {
const query = asRecord(req.query);
const accessToken = getString(query, 'token');
if (!accessToken) {
throw new HttpError(401, 'Token authentication failed', {
legacyCode: 'token_auth_failed',
});
}
const actor =
await this.services.auth.authenticateFromToken(accessToken);
if (!isAccessTokenActor(actor)) {
throw new HttpError(401, 'Token authentication failed', {
legacyCode: 'token_auth_failed',
});
}
// This endpoint authenticates the token by hand and never runs the
// route gate chain, so the suspension and pending-verification checks
// that guard every other authenticated FS route have to run here.
assertNotSuspended(actor!.user);
assertVerifiedAccount(actor!.user);
View on GitHub (pinned to 908ec23eda)
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.
Example fix
// before
fetch('/read?uid=' + uid)
// after
fetch('/read?uid=' + uid + '&token=' + encodeURIComponent(accessToken)) Defensive patterns
Strategy: validation
Validate before calling
// client-side guard before building the /read?token= URL
function buildTokenReadUrl(uid, token) {
if (typeof token !== 'string' || token.trim().length === 0) {
throw new Error('Cannot call /read without an access token');
}
const params = new URLSearchParams({ uid, token: token.trim() });
return `/read?${params.toString()}`;
} Type guard
// narrow a candidate token to a non-empty string
/** @param {unknown} t
* @returns {t is string}
*/
function isNonEmptyToken(t) {
return typeof t === 'string' && t.trim().length > 0;
} Try / catch
try {
const res = await fetch(buildTokenReadUrl(uid, token));
if (res.status === 401) { /* re-auth, then retry once */ }
} catch (e) { /* network error, not the 401 */ } Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- invalid_token
- app_or_api_token_required
- App not found
- Invalid orderBy. Allowed: ${ALLOWED_ORDER_BY.join(', ')}
- bad_request
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/75659e6ac0109ec6.
Report an issue: GitHub.