abhigyanpatwari/GitNexus · error · Error
LLM API error ( )
Error message
LLM API error (${response.status}): ${errorText.slice(0, 500)} What it means
The endpoint returned a non-OK status that resilientFetch does not retry — a terminal 4xx (5xx/429 were already retried and would have surfaced as the 'after retries' error). The message includes the status code and first 500 chars of the response body. Common statuses: 401/403 (bad key), 404 (wrong base URL path or model), 400 (bad request payload/model name).
Solutions
- Read the status + body text: 401/403 → re-authenticate (`gitnexus wiki --api-key ...`), 404 → fix `--base-url` path (usually ends in /v1), 400 → check `--model` spelling
- Verify the endpoint manually: curl -H "Authorization: Bearer $KEY" $BASE_URL/chat/completions with a minimal payload
- For Azure, confirm the deployment name (`--model`) and URL shape https://{resource}.openai.azure.com/openai/v1
- Ensure the key has credit/quota and the model is enabled for the account
Example fix
# before (404 — missing /v1) gitnexus wiki --provider custom --base-url https://api.example.com --model gpt-4o-mini # after gitnexus wiki --provider custom --base-url https://api.example.com/v1 --model gpt-4o-mini
Defensive patterns
Strategy: try-catch
Validate before calling
// Smoke-test credentials and endpoint shape before a long wiki run:
const probe = await fetch(`${baseUrl}/chat/completions`, {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ model, messages: [{ role: 'user', content: 'ping' }], max_tokens: 1 }),
});
if (probe.status === 401 || probe.status === 403 || probe.status === 404) {
throw new Error(`LLM config invalid: ${probe.status} — fix --base-url/--api-key/--model before generating`);
} Try / catch
try {
await callLLM(prompt, config);
} catch (err) {
const m = err instanceof Error ? err.message : '';
if (/LLM API error \((401|403)\)/.test(m)) throw new Error('Re-authenticate: run gitnexus wiki --api-key <key>');
if (/LLM API error \(404\)/.test(m)) throw new Error('Check --base-url (append /v1) and --model name');
throw err;
} Prevention
- Smoke-test the endpoint with a one-token request before launching full wiki generation
- Keep base URL and model name paired in one provider profile so they can't drift
- Rotate keys on a schedule and update ~/.gitnexus/config.json before expiry
When it happens
Trigger: Invalid or revoked `--api-key` (401); missing entitlements for the model (403); `--base-url` pointing to a path that is not an OpenAI-compatible chat-completions endpoint (404); unknown `--model` name (400/404); Azure deployment name mismatch.
Common situations: Expired API key in ~/.gitnexus/config.json; wrong model identifier after a provider renamed models; base URL missing or adding /v1; Azure deployment vs model-name confusion; corporate proxy returning 403.
Related errors
- LLM API error ( after retries)
- --allow-insecure-connection /…
- Azure content filter blocked this request. The prompt…
- Claude CLI not found. Install Claude Code and ensure…
- claude CLI returned empty output
AI-assisted analysis of abhigyanpatwari/GitNexus@52924ef12c (2026-08-20).
Data as JSON: /api/errors/8a1335ea75e00bb1.
Report an issue: GitHub.
Appendix: source
Thrown at gitnexus/src/core/wiki/llm-client.ts:448
}
if (!response.ok) {
const errorText = await response.text().catch(() => 'unknown error');
// Azure content filter — surface a clear message instead of a generic API error.
if (
azure &&
response.status === 400 &&
(errorText.includes('content_filter') || errorText.includes('ResponsibleAIPolicyViolation'))
) {
throw new Error(
`Azure content filter blocked this request. The prompt triggered content policy. Details: ${errorText.slice(0, 300)}`,
);
}
// Any other non-OK response here is a terminal 4xx — resilientFetch
// already retried 5xx/429 to exhaustion and would have thrown above.
throw new Error(`LLM API error (${response.status}): ${errorText.slice(0, 500)}`);
}
// Streaming path
if (useStream && response.body) {
return await readSSEStream(response.body, options!.onChunk!);
}
// Non-streaming path
const json = (await response.json()) as any;
const choice = json.choices?.[0];
if (!choice?.message?.content) {
throw new Error('LLM returned empty response');
}
return {
content: choice.message.content,
promptTokens: json.usage?.prompt_tokens,
completionTokens: json.usage?.completion_tokens,View on GitHub (pinned to 52924ef12c)