{"record":{"id":"1f4425001f61658e","repo":"can1357/oh-my-pi","slug":"http-res-status-from-modelsurl","errorCode":null,"errorMessage":"HTTP ${res.status} from ${modelsUrl}","messagePattern":"HTTP (.+?) from (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/config/model-discovery.ts","lineNumber":831,"sourceCode":"\n\tconst baseHeaders: Record<string, string> = { ...(providerConfig.headers ?? {}) };\n\tlet headers = baseHeaders;\n\tconst timeoutMs = providerConfig.discovery.timeoutMs ?? 10_000;\n\tconst attempt = async (h: Record<string, string>) => {\n\t\tconst nativeMetadataPromise =\n\t\t\tproviderConfig.discovery.type === \"lm-studio\"\n\t\t\t\t? withTimeoutSignal(timeoutMs, signal =>\n\t\t\t\t\t\tfetchLmStudioNativeModelMetadata(baseUrl, ctx.fetch, { headers: h, signal }),\n\t\t\t\t\t)\n\t\t\t\t: Promise.resolve(null);\n\t\tconst [payload, nativeMetadata] = await Promise.all([\n\t\t\twithTimeoutSignal(timeoutMs, async signal => {\n\t\t\t\tconst res = await ctx.fetch(modelsUrl, {\n\t\t\t\t\theaders: h,\n\t\t\t\t\tsignal,\n\t\t\t\t});\n\t\t\t\tif (!res.ok) {\n\t\t\t\t\tthrow new Error(`HTTP ${res.status} from ${modelsUrl}`);\n\t\t\t\t}\n\t\t\t\theaders = h;\n\t\t\t\treturn (await res.json()) as {\n\t\t\t\t\tdata?: Array<{\n\t\t\t\t\t\tid?: string;\n\t\t\t\t\t\tmax_model_len?: unknown;\n\t\t\t\t\t\tcontext_length?: unknown;\n\t\t\t\t\t\tinput?: unknown;\n\t\t\t\t\t\tinput_modalities?: unknown;\n\t\t\t\t\t\tarchitecture?: unknown;\n\t\t\t\t\t}>;\n\t\t\t\t};\n\t\t\t}),\n\t\t\tnativeMetadataPromise,\n\t\t]);\n\t\treturn [payload, nativeMetadata] as const;\n\t};\n\tconst apiKey = await ctx.getBearerApiKeyResolver(providerConfig.provider);","sourceCodeStart":813,"sourceCodeEnd":849,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/config/model-discovery.ts#L813-L849","documentation":"discoverOpenAIModelsList fetches `GET <baseUrl>/models` from an OpenAI-compatible endpoint (including LM Studio) to enumerate models. Any non-ok HTTP status throws `Error(\"HTTP <status> from <modelsUrl>\")`. The default timeout is 10s (overridable via discovery.timeoutMs) and a Bearer key from the provider's resolver is attached when available.","triggerScenarios":"discoverOpenAIModelsList called (via discoverModelsByProviderType) when the /models endpoint returns 401 (missing/invalid API key), 403 (key lacks list permission), 404 (baseUrl wrong, /v1 missing or duplicated), 429 (rate limit), or 5xx from the upstream server/proxy.","commonSituations":"Expired or wrong API key against a hosted OpenAI-compatible gateway; LM Studio server not started (connection refused shows differently, but a proxy in front yields 502); baseUrl missing the /v1 segment for strict OpenAI-compatible servers; self-hosted vLLM/TGI returning 404 because models route is disabled.","solutions":["Test `curl -H \"Authorization: Bearer <key>\" <baseUrl>/models` and match the reported status to fix auth (401/403) or the URL (404).","Set/correct apiKey in the provider config so the Bearer attempt carries a valid credential.","Fix baseUrl to end at the right prefix (usually include /v1 for OpenAI-compatible servers).","If 429/5xx, back off and retry or raise discovery.timeoutMs; check the upstream server/proxy logs."],"exampleFix":"// before: 404 because /v1 missing\n{ \"baseUrl\": \"https://gw.example.com\", \"discovery\": { \"type\": \"models-list\" } }\n// after\n{ \"baseUrl\": \"https://gw.example.com/v1\", \"apiKey\": \"sk-...\", \"discovery\": { \"type\": \"models-list\" } }","handlingStrategy":"validation","validationCode":"const res = await fetch(`${baseUrl.replace(/\\/$/, '')}/models`, { headers: { Authorization: `Bearer ${apiKey}` } });\nif (res.status === 401 || res.status === 403) throw new Error(\"models-list discovery: invalid or missing API key\");\nif (res.status === 404) throw new Error(\"models-list discovery: check baseUrl includes /v1 and the server exposes /models\");","typeGuard":"null","tryCatchPattern":"try {\n  const models = await discoverOpenAIModelsList(cfg, ctx);\n} catch (err) {\n  const status = (err as any)?.message?.match?.(/^HTTP (\\d+)/)?.[1];\n  if (status === \"401\" || status === \"403\") {\n    logger.warn(\"Discovery auth failed; check apiKey\", { baseUrl: cfg.baseUrl });\n  }\n  return bundledModelFallback(cfg.provider);\n}","preventionTips":["Verify key + URL with curl -H \"Authorization: Bearer $KEY\" <baseUrl>/models before configuring.","Include the /v1 prefix for strict OpenAI-compatible gateways.","Rotate/expiry-check API keys; 401 on discovery usually means a stale key.","Set discovery.timeoutMs generously for slow servers and retry on 429/5xx."],"tags":["network","http","model-discovery","openai-compatible"],"backgroundTag":"http-non-2xx-model-list","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}