Fix Elasticsearch CircuitBreakingException: Root Causes and Minimal Checks
Quick Answer
- Conclusion: CircuitBreakingException occurs when Elasticsearch's memory circuit breakers detect that a request would exceed configured JVM heap limits. The fix is either reducing memory pressure from queries/caches or adjusting breaker thresholds.
- First checks: Run
GET /_nodes/stats/breakerto see which breaker triggered, check JVM heap usage viaGET /_nodes/stats/jvm, and review slow query logs for expensive aggregations or large result sets. - Minimal fix: Clear caches with
POST /_cache/clear, optimize queries (pagination, bucket limits, doc_values), and if needed, temporarily raiseindices.breaker.total.limitvia cluster settings. - Environment boundary: Applies to Elasticsearch 7.x and 8.x clusters. Default
indices.breaker.total.limitis 95% of JVM heap; production tuning typically targets 85–90%.
What Problem It Solves
Elasticsearch uses circuit breakers to prevent OutOfMemoryError by estimating memory usage of incoming requests before executing them. When an operation (search, aggregation, bulk indexing) would push estimated memory beyond a configured threshold, the cluster rejects it with a CircuitBreakingException.
This mechanism protects the JVM heap from exhaustion, which would otherwise cause node failure, data loss, or cluster instability. The trade-off is that legitimate requests can be rejected under memory pressure, so understanding and tuning breakers is essential for production stability.
When This Error Appears
CircuitBreakingException typically surfaces in these scenarios:
- High-cardinality aggregations: Terms aggregations on fields with millions of unique values.
- Deep pagination:
from+sizevalues that force large result sets into memory. - Large fielddata usage: Sorting or aggregating on text fields without
doc_values. - Bulk indexing spikes: Concurrent write operations that temporarily spike heap usage.
- Multiple concurrent heavy queries: Parallel requests that collectively exceed the breaker limit.
The error message identifies the breaker type:
[parent]— total memory across all breakers.[fielddata]— fielddata cache for aggregations/sorting.[request]— per-request memory estimation.[in_flight_requests]— HTTP request body sizes.
Root Cause Analysis
The parent circuit breaker calculates the estimated memory of all active requests plus existing caches (fielddata, request cache). When the sum exceeds indices.breaker.total.limit (default 95% of heap), new requests are rejected.
Common root causes:
- Oversized aggregations: Aggregations that produce too many buckets or use
termson high-cardinality fields. - Fielddata on text fields: Text fields are analyzed and not optimized for sorting/aggregation;
doc_values(enabled by default for keyword/numeric fields) are far more memory-efficient. - Cache bloat: Fielddata and request caches accumulate over time, reducing available headroom.
- Heap pressure from other sources: Lucene segment memory, bulk indexing buffers, or the JVM's own overhead can consume heap before breakers trigger.
Minimal Working Configuration
Start with these cluster settings to give yourself headroom while you diagnose the root cause:
JSONPUT /_cluster/settings { "persistent": { "indices.breaker.total.limit": "85%", "indices.breaker.fielddata.limit": "40%", "indices.breaker.request.limit": "60%" } }
These values are conservative starting points. Adjust based on your observed heap usage and query patterns.
For immediate relief during an incident:
BASH# Clear fielddata and request caches POST /_cache/clear # Check current breaker stats GET /_nodes/stats/breaker
Common Errors and Fixes
| Error Pattern | Fix |
|---|---|
[parent] Data too large, data for [<http_request>] would be [X] bytes | Reduce query size, add pagination, or raise indices.breaker.total.limit cautiously |
[fielddata] Data too large, data for [field] would be [X] bytes | Clear fielddata cache, enable doc_values on the field, or raise indices.breaker.fielddata.limit |
[request] Data too large | Reduce size parameter, avoid from + size deep pagination, use search_after instead |
[in_flight_requests] | Reduce bulk batch sizes or HTTP request payload sizes |
Query Optimization Checklist
- Replace
from/sizepagination withsearch_after. - Add
"size": 0to aggregation-only queries. - Limit aggregation buckets with
"terms": {"size": 100}or similar. - Use
doc_valuesfields for sorting and aggregations. - Apply filters before aggregations to reduce the document set.
Production Notes and Security Checks
- Monitor breaker stats: Set up alerts on
GET /_nodes/stats/breakermetrics, especiallytrippedcounts. - Tune JVM heap carefully: Breaker limits are percentages of heap. If heap is too small, even 95% may be insufficient. If heap is too large (>32GB), compressed oops are disabled and memory efficiency drops.
- Dynamic vs static settings: Breaker limits are dynamic and can be changed without restart. Use persistent settings for production changes.
- Security considerations: If you expose Elasticsearch APIs to external tools (like MCP servers), restrict access with RBAC and TLS. Avoid storing credentials in plaintext configuration files.
- Test changes incrementally: Raising breaker limits reduces OOM protection. Always test with realistic query loads before applying to production.
FAQ
Q: How do I determine the right breaker threshold?
A: Start with the defaults (95% total, 60% fielddata, 60% request) and monitor heap usage during peak load. If you see frequent breaker trips but heap usage stays below 85%, your queries are being estimated conservatively — you can raise limits. If heap usage is consistently above 90%, lower limits or scale out nodes instead.
Q: CircuitBreakerException triggered — how do I recover quickly?
A: First, clear caches with POST /_cache/clear. Then identify and kill slow queries (via GET /_tasks?actions=*search* and POST /_tasks/_cancel). If memory pressure persists, temporarily raise indices.breaker.total.limit to 95% while you optimize queries or add nodes.
Q: Does raising breaker limits risk OOM?
A: Yes. The breaker is a safety mechanism. Raising indices.breaker.total.limit above 95% leaves almost no headroom for JVM overhead and Lucene's own memory needs, which can cause OutOfMemoryError and node crashes. Only raise limits when you have verified heap usage patterns and have monitoring in place.