ruvnet/ruflo · error · Error
Unauthorized
Error message
Unauthorized
What it means
Thrown in the API-token auth path when the HuggingFace whoami-v2 call (https://huggingface.co/api/whoami-v2 with the Bearer token) returns a non-2xx response. The token is therefore not accepted by HuggingFace, so the server refuses to authenticate the request.
Source
Thrown at ruflo/src/ruvocal/src/lib/server/auth.ts:483
const user = await collections.users.findOne({ hfUserId: cacheHit.userId });
if (!user) {
throw new Error("User not found");
}
return {
user,
sessionId,
token,
secretSessionId,
isAdmin: user.isAdmin || adminTokenManager.isAdmin(sessionId),
};
}
const response = await fetch("https://huggingface.co/api/whoami-v2", {
headers: { Authorization: `Bearer ${token}` },
});
if (!response.ok) {
throw new Error("Unauthorized");
}
const data = await response.json();
const user = await collections.users.findOne({ hfUserId: data.id });
if (!user) {
throw new Error("User not found");
}
await collections.tokenCaches.insertOne({
tokenHash: hash,
userId: data.id,
createdAt: new Date(),
updatedAt: new Date(),
});
return {
user,
sessionId,View on GitHub (pinned to 6b01dc5a68)
Solutions
- Have the client generate a fresh HF access token and retry.
- Verify the token is sent correctly (no leading 'Bearer ' doubled, no whitespace).
- On 429, implement backoff and retry the whoami call.
- On persistent 5xx, check HuggingFace status and network egress.
Example fix
// before
const res = await fetch('https://huggingface.co/api/whoami-v2', { headers: { Authorization: `Bearer ${token}` } });
if (!res.ok) throw new Error('Unauthorized');
// after
if (!res.ok) {
if (res.status === 429) { /* backoff + retry */ }
throw new Error(`Unauthorized (HF whoami ${res.status})`);
} Defensive patterns
Strategy: retry
Validate before calling
// validate token shape before calling whoami
function looksLikeHfToken(t: string): boolean { return /^hf_[A-Za-z0-9]{20,}$/.test(t.trim()); }
if (!looksLikeHfToken(token)) return res.status(401).json({ error: 'invalid token' }); Type guard
function isPlausibleHfToken(token: string): boolean { return /^hf_[A-Za-z0-9]{20,}$/.test(token.trim()); } Try / catch
try { return await authApi(headers); } catch (e) { if ((e as Error).message === 'Unauthorized') return res.status(401).json({ error: 'HF token invalid or expired' }); throw e; } Prevention
- Have clients refresh expired HF tokens.
- Trim whitespace from tokens before sending.
- Backoff and retry on 429 from HF whoami.
When it happens
Trigger: The Bearer token is expired, revoked, malformed, or not a valid HF token; the token cache missed (or was evicted) so the code falls through to the live whoami call, which responds 401/403/429. Also possible if huggingface.co is unreachable and returns a 5xx.
Common situations: User's HF token expired or was revoked in account settings; token was typo'd or pasted with extra whitespace; rate limited by HF (429); a network outage or HF-side incident returns 5xx; the app's IP was temporarily blocked.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- User token not found
- SSRF guard: private/loopback host rejected — ${host}
- Failed to create share link
- User not found
- MCP server "${server.name}" returned HTTP ${httpStatus}: ${h
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/95287ac31371e962.
Report an issue: GitHub.