{"record":{"id":"c7ab6fc53b766880","repo":"abhigyanpatwari/GitNexus","slug":"llm-api-error-err-response-status-after-retrie","errorCode":null,"errorMessage":"LLM API error (${err.response.status} after retries): ${errorText.slice(0, 500)}","messagePattern":"LLM API error \\((.+?) after retries\\): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/wiki/llm-client.ts","lineNumber":415,"sourceCode":"        signal:\n          config.requestTimeoutMs !== undefined\n            ? AbortSignal.timeout(config.requestTimeoutMs)\n            : undefined,\n      },\n      {\n        breakerKey: `wiki-llm-${new URL(url).host}`,\n        retry: { maxAttempts: config.maxAttempts ?? 3, baseDelayMs: 2_000, capDelayMs: 30_000 },\n      },\n    );\n  } catch (err) {\n    if (err instanceof CircuitOpenError) {\n      throw new Error(\n        `LLM endpoint circuit open: retry in ${Math.ceil(err.retryAfterMs / 1000)}s. ${err.message}`,\n      );\n    }\n    if (err instanceof ResilientFetchExhaustedError) {\n      const errorText = await err.response.text().catch(() => 'unknown error');\n      throw new Error(\n        `LLM API error (${err.response.status} after retries): ${errorText.slice(0, 500)}`,\n      );\n    }\n    if (config.requestTimeoutMs !== undefined && isTimeoutLikeError(err)) {\n      throw new Error(\n        `LLM request timed out after ${formatTimeoutDuration(config.requestTimeoutMs)}. ` +\n          'Increase --timeout or omit it to disable the request timeout.',\n      );\n    }\n    throw err;\n  }\n\n  if (!response.ok) {\n    const errorText = await response.text().catch(() => 'unknown error');\n\n    // Azure content filter — surface a clear message instead of a generic API error.\n    if (\n      azure &&","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/gitnexus/src/core/wiki/llm-client.ts#L397-L433","documentation":"Thrown when callLLM() catches a ResilientFetchExhaustedError: resilientFetch retried the request up to config.maxAttempts (default 3) against a non-OK HTTP response and gave up. The message includes the final HTTP status code and up to 500 chars of the response body so you can see the provider's own error text. Retriable statuses (5xx, 429) were already attempted; this catch typically means a hard failure.","triggerScenarios":"Provider returns 401/403 (bad/expired API key), 404 (wrong base URL or model name), 400 (malformed request body), or persistent 5xx/429 after all retries. Example: baseUrl='https://api.openai.com/v1' with model='gpt-9-fake' yields 'LLM API error (404 after retries): The model `gpt-9-fake` does not exist'.","commonSituations":"Wrong API key or expired token; model name typo or a model the account lacks access to; baseUrl missing the /v1 suffix or pointing at the wrong deployment; provider returning 429 because the org hit a quota; Azure deployment name mismatch.","solutions":["Read the 500-char body slice in the message — it is the provider's own explanation (e.g. 'invalid_api_key', 'model_not_found').","For 401/403: verify the API key env var (GITNEXUS_LLM_API_KEY / OPENAI_API_KEY) is set, valid, and has credit/quota.","For 404: confirm the baseUrl path (most need /v1) and the exact model id the provider exposes.","For 429: lower concurrency, raise config.maxAttempts, or add backoff via requestTimeoutMs.","For 5xx: check the provider status page; the breaker (error 221) may also open."],"exampleFix":"// before\nconfig = { baseUrl: 'https://api.openai.com', model: 'gpt-4o', apiKey };\n// 404: missing /v1, or model id typo\n\n// after\nconfig = { baseUrl: 'https://api.openai.com/v1', model: 'gpt-4o-mini', apiKey };","handlingStrategy":"try-catch","validationCode":"async function preflight(baseUrl, apiKey, model) {\n  const r = await fetch(new URL('/models', baseUrl), { headers: { Authorization: 'Bearer ' + apiKey } });\n  if (!r.ok) throw new Error(`preflight ${r.status}`);\n  const ids = (await r.json()).data.map(m => m.id);\n  if (!ids.includes(model)) throw new Error(`unknown model ${model}`);\n}","typeGuard":"function isApiStatusError(e) {\n  return e instanceof Error && /LLM API error \\(\\d+ after retries\\)/.test(e.message);\n}\nfunction apiStatus(e) { const m = /\\((\\d+) after retries\\)/.exec(e.message||''); return m ? +m[1] : null; }","tryCatchPattern":"try { return await callLLM(prompt, config); }\ncatch (e) {\n  const s = apiStatus(e);\n  if (s === 401 || s === 403) { /* refresh key */ }\n  else if (s === 404) { /* fix baseUrl/model */ }\n  else if (s === 429) { await sleep(5000); return await callLLM(prompt, config); }\n  throw e;\n}","preventionTips":["Preflight the API key and model id against GET /models before the long generation.","Ensure baseUrl ends with the provider-required path (usually /v1).","Log the 500-char body slice — it is the provider's own diagnosis."],"tags":["llm","network","api-error","retry","wiki"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}