abhigyanpatwari/GitNexus · error · Error
LLM endpoint circuit open: retry in ${Math.ceil(err.retryAft
Error message
LLM endpoint circuit open: retry in ${Math.ceil(err.retryAfterMs / 1000)}s. ${err.message} What it means
Thrown when callLLM() catches a CircuitOpenError from resilientFetch(). GitNexus wraps each LLM host in an in-process circuit breaker keyed 'wiki-llm-<host>'; after enough consecutive failures the breaker opens and fails fast instead of hammering a sick endpoint. The message surfaces the remaining cool-down (retryAfterMs rounded up to seconds) plus the underlying cause. This protects both your run and the upstream provider from a retry storm.
Source
Thrown at gitnexus/src/core/wiki/llm-client.ts:409
...authHeaders,
},
body: JSON.stringify(body),
// Request timeout is opt-in for wiki generation. Large local
// model runs can legitimately take well over a minute, so the
// default runtime path must not impose a hidden 60s ceiling.
signal:
config.requestTimeoutMs !== undefined
? AbortSignal.timeout(config.requestTimeoutMs)
: undefined,
},
{
breakerKey: `wiki-llm-${new URL(url).host}`,
retry: { maxAttempts: config.maxAttempts ?? 3, baseDelayMs: 2_000, capDelayMs: 30_000 },
},
);
} catch (err) {
if (err instanceof CircuitOpenError) {
throw new Error(
`LLM endpoint circuit open: retry in ${Math.ceil(err.retryAfterMs / 1000)}s. ${err.message}`,
);
}
if (err instanceof ResilientFetchExhaustedError) {
const errorText = await err.response.text().catch(() => 'unknown error');
throw new Error(
`LLM API error (${err.response.status} after retries): ${errorText.slice(0, 500)}`,
);
}
if (config.requestTimeoutMs !== undefined && isTimeoutLikeError(err)) {
throw new Error(
`LLM request timed out after ${formatTimeoutDuration(config.requestTimeoutMs)}. ` +
'Increase --timeout or omit it to disable the request timeout.',
);
}
throw err;
}
View on GitHub (pinned to d540b00184)
Solutions
- Wait the indicated cool-down (retryAfterMs) before retrying — the breaker auto-closes after it elapses.
- Check the underlying err.message echoed in the text to fix the root cause (API key, model name, endpoint health).
- Verify the provider status page and the endpoint reachability (curl the /models endpoint).
- Restart a crashed local model server, or point baseUrl at a healthy host (different breaker key).
- If the breaker opens during long-running generation, lower config.maxAttempts or add requestTimeoutMs to surface timeouts sooner rather than burning attempts.
Example fix
// before
for (const p of prompts) {
await callLLM(p, config); // hammers a sick endpoint, trips breaker
}
// after: respect cool-down + jitter
async function callWithBreaker(p) {
try { return await callLLM(p, config); }
catch (e) {
const m = /retry in (\d+)s/.exec(e.message);
if (m) await sleep((+m[1]) * 1000 + 500);
throw e;
}
} Defensive patterns
Strategy: retry
Validate before calling
// No pre-call check exists; circuit state is internal. Probe endpoint health first:
async function endpointHealthy(baseUrl) {
try {
const r = await fetch(new URL('/models', baseUrl), { headers: { Authorization: 'Bearer ' + key } });
return r.ok || r.status === 404; // 404 = wrong path but reachable
} catch { return false; }
} Type guard
function isCircuitOpenError(e) {
return e instanceof Error && /circuit open: retry in \d+s/.test(e.message);
}
function circuitRetrySeconds(e) {
const m = /retry in (\d+)s/.exec(e.message || '');
return m ? +m[1] : null;
} Try / catch
try { return await callLLM(prompt, config); }
catch (e) {
if (isCircuitOpenError(e)) {
const s = circuitRetrySeconds(e);
if (s) await new Promise(r => setTimeout(r, s * 1000 + 500));
return await callLLM(prompt, config); // one retry after cool-down
}
throw e;
} Prevention
- Fix auth/endpoint issues fast so consecutive failures never reach the breaker threshold.
- Keep maxAttempts low (3) so each failed call does not widen the failure window.
- Watch for the breaker message in logs and back off the whole job rather than spawning more calls.
When it happens
Trigger: Three or more consecutive failed LLM calls to the same host within the breaker window (e.g. repeated 5xx, repeated auth failures, or repeated timeouts). The next callLLM() short-circuits before any network request and you see 'circuit open: retry in Ns'. Reproducible by pointing at an endpoint with a bad API key and forcing several calls quickly.
Common situations: LLM provider regional outage; wrong API key returning 401/403 every attempt; sustained 429 rate limiting that exhausts retries; local model server (Ollama) crashed but baseUrl still points at it; CI run hammering the endpoint after maxAttempts=3 each.
Related errors
- LLM API error (${err.response.status} after retries): ${erro
- Circuit '${key}' is open; retry in ${Math.ceil(retryAfterMs
- Request failed after retries (HTTP ${response.status})
- hf-circuit-open: HuggingFace download circuit is open after
- hf-circuit-open: HuggingFace download circuit opened after 3
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/f0bebf9dc64ed4c7.
Report an issue: GitHub.