abhigyanpatwari/GitNexus · error · Error
LLM API error (${response.status}): ${errorText.slice(0, 500
Error message
LLM API error (${response.status}): ${errorText.slice(0, 500)} What it means
The catch-all terminal-error path in callLLM(): resilientFetch returned a Response whose .ok is false (HTTP >= 400), it was not the Azure content-filter case, and resilientFetch already exhausted retries for transient statuses. The message includes the HTTP status and up to 500 chars of body. This is reached for non-retryable 4xx that resilientFetch chose not to retry (e.g. 400/401/403/404/422).
Source
Thrown at gitnexus/src/core/wiki/llm-client.ts:444
}
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 d540b00184)
Solutions
- Inspect the status code and the 500-char body in the message — they identify the problem.
- For 400/422: check the request body — drop temperature for reasoning models, ensure max_completion_tokens is set, validate the provider's schema.
- For 401/403: fix the API key and the auth scheme (Azure uses api-key header, others use Bearer).
- For 404: verify baseUrl path and model id.
- If the body shows a transient cause that should have been retried, file a bug — resilientFetch should have caught it.
Example fix
// before
await callLLM(prompt, { baseUrl, apiKey, model: 'o1-preview', temperature: 0.7 });
// 400: reasoning models reject temperature
// after
await callLLM(prompt, { baseUrl, apiKey, model: 'o1-preview' }); // no temperature Defensive patterns
Strategy: try-catch
Validate before calling
async function probeProviderSchema(baseUrl, apiKey, model) {
// tiny request that should be 200 if schema is right
const r = await fetch(baseUrl.replace(/\/+$/, '') + '/chat/completions', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + apiKey },
body: JSON.stringify({ model, messages: [{ role: 'user', content: 'ping' }], max_completion_tokens: 1 }),
});
return r.status;
} Type guard
function isTerminalApiError(e) {
return e instanceof Error && /LLM API error \(\d+\):/.test(e.message);
}
function terminalStatus(e) { const m = /LLM API error \((\d+)\):/.exec(e.message||''); return m ? +m[1] : null; } Try / catch
try { return await callLLM(prompt, config); }
catch (e) {
const s = terminalStatus(e);
if (s === 400 || s === 422) { /* drop unsupported param (e.g. temperature on reasoning) */ }
else if (s === 401 || s === 403) { /* fix auth header */ }
else if (s === 404) { /* fix baseUrl path / model id */ }
throw e;
} Prevention
- Probe a 1-token request first to validate schema/auth before long generation.
- Match the auth scheme to the provider (Azure: api-key header; others: Bearer).
- Do not send temperature to reasoning models; do not omit max_completion_tokens.
When it happens
Trigger: Provider returns a 4xx that is not retried: 400 malformed body, 401/403 auth, 404 not found, 422 unprocessable, or a custom 4xx from a proxy. Because resilientFetch only retries 5xx/429, such responses fall through to response.ok===false and hit this throw.
Common situations: Wrong Authorization header format for the provider; unsupported request parameter (e.g. sending temperature to a reasoning model); proxy returning 407 auth required; baseUrl missing path component; model id rejected with 422.
Related errors
- LLM API error (${err.response.status} after retries): ${erro
- LLM endpoint circuit open: retry in ${Math.ceil(err.retryAft
- LLM request timed out after ${formatTimeoutDuration(config.r
- LLM returned empty response
- Insecure http:// LLM base URLs are only allowed for localhos
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/8a1335ea75e00bb1.
Report an issue: GitHub.