{"record":{"id":"95287ac31371e962","repo":"ruvnet/ruflo","slug":"unauthorized","errorCode":null,"errorMessage":"Unauthorized","messagePattern":"Unauthorized","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ruflo/src/ruvocal/src/lib/server/auth.ts","lineNumber":483,"sourceCode":"\t\t\t\tconst user = await collections.users.findOne({ hfUserId: cacheHit.userId });\n\t\t\t\tif (!user) {\n\t\t\t\t\tthrow new Error(\"User not found\");\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tuser,\n\t\t\t\t\tsessionId,\n\t\t\t\t\ttoken,\n\t\t\t\t\tsecretSessionId,\n\t\t\t\t\tisAdmin: user.isAdmin || adminTokenManager.isAdmin(sessionId),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst response = await fetch(\"https://huggingface.co/api/whoami-v2\", {\n\t\t\t\theaders: { Authorization: `Bearer ${token}` },\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new Error(\"Unauthorized\");\n\t\t\t}\n\n\t\t\tconst data = await response.json();\n\t\t\tconst user = await collections.users.findOne({ hfUserId: data.id });\n\t\t\tif (!user) {\n\t\t\t\tthrow new Error(\"User not found\");\n\t\t\t}\n\n\t\t\tawait collections.tokenCaches.insertOne({\n\t\t\t\ttokenHash: hash,\n\t\t\t\tuserId: data.id,\n\t\t\t\tcreatedAt: new Date(),\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tuser,\n\t\t\t\tsessionId,","sourceCodeStart":465,"sourceCodeEnd":501,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/ruflo/src/ruvocal/src/lib/server/auth.ts#L465-L501","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst res = await fetch('https://huggingface.co/api/whoami-v2', { headers: { Authorization: `Bearer ${token}` } });\nif (!res.ok) throw new Error('Unauthorized');\n\n// after\nif (!res.ok) {\n  if (res.status === 429) { /* backoff + retry */ }\n  throw new Error(`Unauthorized (HF whoami ${res.status})`);\n}","handlingStrategy":"retry","validationCode":"// validate token shape before calling whoami\nfunction looksLikeHfToken(t: string): boolean { return /^hf_[A-Za-z0-9]{20,}$/.test(t.trim()); }\nif (!looksLikeHfToken(token)) return res.status(401).json({ error: 'invalid token' });","typeGuard":"function isPlausibleHfToken(token: string): boolean { return /^hf_[A-Za-z0-9]{20,}$/.test(token.trim()); }","tryCatchPattern":"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; }","preventionTips":["Have clients refresh expired HF tokens.","Trim whitespace from tokens before sending.","Backoff and retry on 429 from HF whoami."],"tags":["auth","huggingface","token","network","ruvocal"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}