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

  1. Have the client generate a fresh HF access token and retry.
  2. Verify the token is sent correctly (no leading 'Bearer ' doubled, no whitespace).
  3. On 429, implement backoff and retry the whoami call.
  4. 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

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

Related errors


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/95287ac31371e962. Report an issue: GitHub.