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

  1. Wait for the breaker cooldown to elapse — it will half-open and retry automatically on the next call
  2. Fix the root cause first (key, quota, connectivity) so the half-open probe succeeds
  3. Restart the process to reset breaker state if the window is inappropriate for your workload
  4. Catch this error and queue work for later retry instead of failing the batch
  5. 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

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.