rohitg00/agentmemory · error · Error
circuit_breaker_open
circuit_breaker_open
Error message
circuit_breaker_open
What it means
ResilientProvider.call checks the CircuitBreaker before delegating to the wrapped MemoryProvider; when the breaker is open (too many recent failures) it throws immediately with the coded error 'circuit_breaker_open' without any network attempt. This is intentional fast-fail backpressure: it protects the upstream LLM API while it is considered unhealthy and lets it recover.
Source
Thrown at src/providers/resilient.ts:14
import type { MemoryProvider, CircuitBreakerState } from "../types.js";
import { CircuitBreaker } from "./circuit-breaker.js";
export class ResilientProvider implements MemoryProvider {
private breaker = new CircuitBreaker();
name: string;
constructor(private inner: MemoryProvider) {
this.name = `resilient(${inner.name})`;
}
private async call(fn: () => Promise<string>): Promise<string> {
if (!this.breaker.isAllowed) {
throw new Error("circuit_breaker_open");
}
try {
const result = await fn();
this.breaker.recordSuccess();
return result;
} catch (err) {
this.breaker.recordFailure();
throw err;
}
}
async compress(systemPrompt: string, userPrompt: string): Promise<string> {
return this.call(() => this.inner.compress(systemPrompt, userPrompt));
}
async summarize(systemPrompt: string, userPrompt: string): Promise<string> {
return this.call(() => this.inner.summarize(systemPrompt, userPrompt));
}View on GitHub (pinned to e04ba88819)
Solutions
- Wait for the breaker cooldown to elapse — it will half-open and retry automatically on the next call
- Fix the root cause first (key, quota, connectivity) so the half-open probe succeeds
- Restart the process to reset breaker state if the window is inappropriate for your workload
- Catch this error and queue work for later retry instead of failing the batch
- Tune breaker thresholds/cooldown in resilient.ts to match your request volume
Example fix
// before
for (const doc of docs) await provider.compress(doc); // throws circuit_breaker_open mid-batch
// after
for (const doc of docs) {
try { await provider.compress(doc); }
catch (e) { if (e.message === 'circuit_breaker_open') { await sleep(5000); retry(doc); } }
} Defensive patterns
Strategy: retry
Type guard
function isCircuitOpen(e: unknown): e is Error & { message: 'circuit_breaker_open' } {
return e instanceof Error && e.message === 'circuit_breaker_open';
} Try / catch
try {
return await resilientProvider.call(prompt);
} catch (e) {
if (isCircuitOpen(e)) {
await sleep(jitteredBackoff());
return resilientProvider.call(prompt);
}
throw e;
} Prevention
- Fix upstream root causes (keys, quota, outages) promptly so the breaker can close again
- Queue/defer work instead of tight-looping while the breaker is open
- Size breaker thresholds and cooldown to your real traffic volume
- Reset breaker state between test cases to avoid cross-test flakiness
When it happens
Trigger: compress()/summarize() called while the breaker for the inner provider is open — i.e. the failure threshold was reached within the breaker's rolling window and the cooldown has not elapsed; every call during the open window throws this instantly.
Common situations: Bulk operations looping compress/summarize after an outage or key expiry trips the breaker; a burst of 429/5xx responses opening the breaker, then queued jobs hammering it; long cooldown misread as a 'stuck' provider; tests run without resetting breaker state between cases.
AI-assisted analysis of rohitg00/agentmemory@e04ba88819 (2026-08-30).
Data as JSON: /api/errors/8619efa11ad8a436.
Report an issue: GitHub.