{"record":{"id":"cae8577824a0a769","repo":"ruvnet/ruflo","slug":"failed-to-fetch-baseurl-models-response-stat","errorCode":null,"errorMessage":"Failed to fetch ${baseURL}/models: ${response.status} ${response.statusText} (no auth token available)","messagePattern":"Failed to fetch (.+?)/models: (.+?) (.+?) \\(no auth token available\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ruflo/src/ruvocal/src/lib/server/models.ts","lineNumber":320,"sourceCode":"\t\tthrow new Error(\"OPENAI_BASE_URL not set\");\n\t}\n\n\ttry {\n\t\tconst baseURL = openaiBaseUrl;\n\t\tlogger.info({ baseURL }, \"[models] Using OpenAI-compatible base URL\");\n\n\t\t// Canonical auth token is OPENAI_API_KEY; keep HF_TOKEN as legacy alias\n\t\tconst authToken = config.OPENAI_API_KEY || config.HF_TOKEN;\n\n\t\t// Use auth token from the start if available to avoid rate limiting issues\n\t\t// Some APIs rate-limit unauthenticated requests more aggressively\n\t\tconst response = await fetch(`${baseURL}/models`, {\n\t\t\theaders: authToken ? { Authorization: `Bearer ${authToken}` } : undefined,\n\t\t});\n\t\tlogger.info({ status: response.status }, \"[models] First fetch status\");\n\t\tif (!response.ok && response.status === 401 && !authToken) {\n\t\t\t// If we get 401 and didn't have a token, there's nothing we can do\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to fetch ${baseURL}/models: ${response.status} ${response.statusText} (no auth token available)`\n\t\t\t);\n\t\t}\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to fetch ${baseURL}/models: ${response.status} ${response.statusText}`\n\t\t\t);\n\t\t}\n\t\tconst json = await response.json();\n\t\tlogger.info({ keys: Object.keys(json || {}) }, \"[models] Response keys\");\n\n\t\tconst parsed = listSchema.parse(json);\n\t\tlogger.info({ count: parsed.data.length }, \"[models] Parsed models count\");\n\n\t\tlet modelsRaw = parsed.data.map((m) => {\n\t\t\tlet logoUrl: string | undefined = undefined;\n\t\t\tif (isHFRouter && m.id.includes(\"/\")) {\n\t\t\t\tconst org = m.id.split(\"/\")[0];","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/ruflo/src/ruvocal/src/lib/server/models.ts#L302-L338","documentation":"Thrown by buildModels() when the GET ${OPENAI_BASE_URL}/models request returns 401 AND no auth token is configured. The token is OPENAI_API_KEY (canonical) or HF_TOKEN (legacy alias); with neither set and an unauthorized response, retrying cannot help, so the code fails fast with the '(no auth token available)' suffix instead of looping on a doomed request.","triggerScenarios":"fetch(baseURL + '/models') with no Authorization header responds 401 because neither config.OPENAI_API_KEY nor config.HF_TOKEN is set. Typical for gateways (e.g. Hugging Face router) that require a Bearer token even for the model list.","commonSituations":"Deployments that copied .env but left OPENAI_API_KEY blank assuming the gateway was public; migration from HF_TOKEN-only setups where the alias was removed; token present under a different variable name.","solutions":["Set OPENAI_API_KEY (or legacy HF_TOKEN) in .env.local and restart the server.","Verify the token is valid for the gateway: curl -H \"Authorization: Bearer $OPENAI_API_KEY\" $OPENAI_BASE_URL/models returns 200.","If the gateway truly serves anonymous model lists, check for a proxy in front that injects its own 401 (corporate proxy, auth middleware).","Make sure the token has no trailing newline/quotes in the env file."],"exampleFix":"# .env.local (before)\nOPENAI_BASE_URL=https://router.huggingface.co/v1\n\n# .env.local (after)\nOPENAI_BASE_URL=https://router.huggingface.co/v1\nOPENAI_API_KEY=hf_xxxxxxxxxxxx","handlingStrategy":"validation","validationCode":"const token = process.env.OPENAI_API_KEY || process.env.HF_TOKEN;\nif (!token) {\n\tthrow new Error(\"OPENAI_API_KEY (or HF_TOKEN) is required by this gateway\");\n}","typeGuard":null,"tryCatchPattern":"try {\n\tawait buildModels();\n} catch (err) {\n\tif (String(err).includes(\"no auth token available\")) {\n\t\t// configuration problem — do not retry, surface to operator\n\t\tprocess.exitCode = 1;\n\t}\n\tthrow err;\n}","preventionTips":["Verify the token with a curl to /models during provisioning.","Treat 401-without-token as a permanent config error: never auto-retry it.","Prefer the canonical OPENAI_API_KEY name over the HF_TOKEN alias in new deployments."],"tags":["authentication","http-401","environment","models"],"backgroundTag":"api-request-unauthorized","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}