How OmniRoute's Circuit Breaker Differentiates Provider-Level vs Account-Level Errors
OmniRoute uses a two-layer resilience system: provider-level circuit breakers block entire upstream providers when systemic failures occur, while account-level cooldowns isolate individual credentials when only one API key or token fails.
The diegosouzapw/OmniRoute routing engine implements granular fault tolerance that prevents a single bad credential from taking down an entire provider, while also protecting against thundering-herd scenarios during upstream outages. This dual-layer approach lives primarily in src/shared/utils/circuitBreaker.ts and open-sse/services/accountFallback.ts.
Provider-Level Circuit Breakers: Protecting Against Systemic Failures
OmniRoute aggregates failures across all connections to a provider using the CircuitBreaker class. When failure rates exceed configured thresholds, the entire provider is temporarily blocked.
How Provider Breakers Are Triggered
In open-sse/services/accountFallback.ts, errors are classified by HTTP status code. Only failures matching PROVIDER_FAILURE_ERROR_CODES (defined at lines 226-227) propagate to the provider-level breaker:
408 (Request Timeout), 429 (Rate Limit), and 500-504 (Server Errors)
These indicate infrastructure problems rather than credential-specific issues.
// From open-sse/services/accountFallback.ts
const PROVIDER_FAILURE_ERROR_CODES = [408, 429, 500, 501, 502, 503, 504];
The breaker singleton is retrieved via getCircuitBreaker(provider) and updated through CircuitBreaker.execute() in the request pipeline.
Deduplication Prevents Inflation
To avoid skewing provider breaker metrics from rapid retries on the same failing connection, OmniRoute maintains two deduplication windows:
lastConnectionFailure: 5-second window per connectionlastNetworkErrorByProvider: 10-second window per provider
Implemented at lines 28-41 of accountFallback.ts, these ensure that a single network blip doesn't immediately open the provider breaker, while still capturing genuinely distributed failures.
State Transitions in circuitBreaker.ts
The CircuitBreaker class manages three states:
CLOSED: Normal operation, failures trackedOPEN: All requests short-circuited,canExecute()returnsfalseHALF_OPEN: Testing recovery with limited traffic
State transitions occur in _onFailure(kind) (lines 94-106 of circuitBreaker.ts). When failureThreshold is exceeded, the breaker moves to OPEN for resetTimeout milliseconds.
Account-Level Cooldowns: Isolating Individual Credentials
When an error doesn't indicate systemic provider failure—such as 401 Unauthorized, 403 Forbidden, or model-specific quota exhaustion—OmniRoute handles it at the connection level.
Connection State Tracking
Per-account resilience is configured through ProviderProfile fields in accountFallback.ts (lines 20-30):
| Field | Purpose |
|---|---|
baseCooldownMs |
Initial backoff duration for this account |
maxBackoffSteps |
Maximum exponential backoff iterations |
rateLimitCooldown |
Specific duration for 429 responses |
The Account Availability Pipeline
- Error classification: Functions like
classify429FromErrorcategorize failures - State recording:
markAccountUnavailablewritesrateLimitedUntil,backoffLevel, andlastErrorto the database - Execution check:
canExecutein request handlers queriesisAccountUnavailable(lines 41-44) to skip cooled connections - Recovery:
recordProviderSuccessclears cooldowns when requests succeed
// Conceptual flow from accountFallback.ts
if (errorStatus === 429 && error.code === 'insufficient_quota') {
// Account-level: only this API key is affected
await markAccountUnavailable(connectionId, 'quota_exhausted', backoffMs);
} else if (PROVIDER_FAILURE_ERROR_CODES.includes(errorStatus)) {
// Provider-level: contributes to aggregate circuit breaker
getCircuitBreaker(provider).recordFailure();
}
Decision Flow: Where Errors Get Routed
Incoming Error
│
▼
┌─────────────────┐
│ HTTP Status Code│
│ Classification │
└────────┬────────┘
│
┌────┴────┐
▼ ▼
408,429,5xx 401,403,quota
(PROVIDER) (ACCOUNT)
│ │
▼ ▼
┌─────────┐ ┌─────────────────┐
│Deduplicate│ │markAccountUnavailable│
│ (5s/10s) │ │ update DB row │
└────┬────┘ └─────────────────┘
│
▼
┌─────────────────┐
│getCircuitBreaker│
│ (provider) │
│ .recordFailure()│
└────────┬────────┘
▼
Threshold Check
(failureThreshold)
│
┌────┴────┐
▼ ▼
CLOSED THRESHOLD EXCEEDED
│
▼
OPEN
(all provider requests blocked)
Configuration and Operational Controls
Inspecting Breaker Status
import { getCircuitBreaker } from "@/shared/utils/circuitBreaker";
const cb = getCircuitBreaker("openai");
console.log(cb.getStatus());
// {
// name: "openai",
// state: "CLOSED",
// failureCount: 0,
// retryAfterMs: 0,
// ...
// }
Checking Individual Account Availability
import { getProviderCredentials } from "@omniroute/open-sse/services/auth";
const creds = await getProviderCredentials("openai", null, null, null, {
allowRateLimitedConnections: false,
});
if (!creds) {
console.log("All OpenAI accounts are currently rate‑limited or unavailable");
}
Emergency Reset
import { resetAllCircuitBreakers } from "@/shared/utils/circuitBreaker";
resetAllCircuitBreakers(); // Clears provider-level state post-deployment
Key Source Files
| File | Responsibility | Link |
|---|---|---|
src/shared/utils/circuitBreaker.ts |
Generic breaker implementation, provider registry, persistence | circuitBreaker.ts |
open-sse/services/accountFallback.ts |
Error classification, deduplication, account cooldown logic | accountFallback.ts |
src/lib/resilience/settings.ts |
Default breaker profiles (failureThreshold, resetTimeout) |
settings.ts |
src/lib/monitoring/providerHealthMatrix.ts |
Health API exposure of breaker states | providerHealthMatrix.ts |
Summary
-
Provider-level circuit breakers in
circuitBreaker.tsprotect against upstream infrastructure failures using aggregated failure counts across all connections, triggered by HTTP 408/429/5xx status codes with 5-10 second deduplication windows. -
Account-level cooldowns in
accountFallback.tsisolate individual credentials through database-trackedrateLimitedUntiltimestamps and exponential backoff, preserving healthy accounts when one API key fails. -
Dual-layer classification happens at error time: systemic codes hit the provider breaker, credential-specific issues trigger per-account throttling—ensuring neither noisy neighbors nor upstream outages degrade routing availability.
Frequently Asked Questions
How do I know if a provider is in OPEN state versus just having rate-limited accounts?
Check the provider breaker directly via getCircuitBreaker(provider).getStatus(). When state equals "OPEN", all accounts are blocked. If state is "CLOSED" but requests still fail, individual accounts likely have active cooldowns—query isAccountUnavailable for specific connection IDs.
Can I adjust the failure threshold for a specific provider?
Yes. Pass a custom profile when initializing the breaker: getCircuitBreaker("anthropic", { failureThreshold: 5, resetTimeout: 30000 }). The default profiles are defined in src/lib/resilience/settings.ts and applied at accountFallback.ts lines 84-89.
Why does OmniRoute deduplicate provider failures instead of counting every error?
The 5-second lastConnectionFailure and 10-second lastNetworkErrorByProvider windows prevent scenario where rapid retries on one bad connection artificially inflate the aggregate failure rate. This ensures the provider breaker genuinely reflects distributed outages, not localized retry storms.
What happens when a provider breaker is OPEN but some accounts still have quota?
All traffic to that provider is rejected regardless of individual account health. This is intentional: if OpenAI returns 503s across multiple accounts, attempting alternate credentials wastes latency and may exacerbate the upstream degradation. Accounts resume eligibility once the breaker transitions to HALF_OPEN after resetTimeout.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →