Elasticsearch Bulk API Batch Size Exceeded During CI/CD Data Ingestion
Problem Description
During automated model‑training pipelines, a bulk indexing step fails with HTTP 413 or a BulkIndexError. Typical log excerpts include:
[2026-06-30T12:14:02,871][ERROR][o.e.b.BulkProcessor] [node-1] request size exceeds the configured limit
org.elasticsearch.ElasticsearchException: request size exceeds the configured limit
at org.elasticsearch.http.netty4.Netty4HttpChannelHandler.handleRequest(Netty4HttpChannelHandler.java:215)
...
Caused by: java.lang.IllegalArgumentException: request size exceeds the configured limit
Other observed symptoms:
- CI job aborts after the first bulk call (e.g., GitLab CI, Jenkins, Azure DevOps).
- Metrics show a spike in
thread_pool.bulk.rejectedandhttp.total_requestscounters. - Only the largest batch fails; smaller batches succeed.
Root Cause Analysis
Elasticsearch enforces a hard limit on the HTTP request body size via the http.max_content_length setting (default 100mb). The Bulk API documentation states that any bulk payload larger than this limit is rejected with a 413 response (Bulk API docs).
In CI/CD pipelines the ingestion step often aggregates a full training dataset (hundreds of megabytes) into a single bulk request to reduce network round‑trips. When the aggregated size crosses the http.max_content_length threshold, Elasticsearch returns:
HTTP/1.1 413 Request Entity Too Large
{
"error": {
"type": "illegal_argument_exception",
"reason": "request size exceeds the configured limit"
},
"status": 413
}
Two additional constraints can amplify the failure:
- Thread‑pool bulk queue saturation:
thread_pool.bulk.queue_sizedefaults to 200. Parallel CI agents can flood the bulk thread pool, leading to 429 “Too many requests” errors (Cluster Settings). - Client‑side bulk helpers (e.g.,
elasticsearch-py.helpers.bulk) often buffer a configurable number of actions before sending. If thechunk_sizeormax_chunk_bytesis set too high, a single request can exceed the server limit (Bulk Processor Best Practices).
Investigation and Debugging Steps
- Confirm the exact error from Elasticsearch logs.
journalctl -u elasticsearch | grep "request size exceeds"Expected output:
Oct 12 03:22:14 node-1 elasticsearch[12345]: [WARN ][o.e.b.BulkProcessor] [node-1] request size exceeds the configured limit - Measure the bulk payload size generated by the CI job.
# Assuming the CI job writes the bulk payload to a temporary file du -h bulk_payload.json # Example output: 150M bulk_payload.json - Inspect the client configuration. For Python:
from elasticsearch import Elasticsearch, helpers es = Elasticsearch( ["https://es-prod.example.com:9200"], http_auth=("elastic", "****"), maxsize=25, timeout=30, ) # Bulk helper defaults helpers.bulk(es, actions, chunk_size=5000) # <-- may produce >100 MB chunks - Check server‑side limits.
curl -s -XGET "https://es-prod.example.com:9200/_cluster/settings?include_defaults=true" | jq '.defaults.http.max_content_length' # Example output: "100mb" - Validate thread‑pool saturation.
curl -s "https://es-prod.example.com:9200/_cat/thread_pool/bulk?v" # Output columns: active, queue, rejected, completedIf
queuefrequently hits thequeue_sizelimit (default 200), consider reducing parallelism.
Solution
The fix consists of two parts: adjust the bulk request size on the client side and, if necessary, tune the Elasticsearch server limits.
Client‑Side Adjustments
Split the payload into smaller chunks that stay comfortably below the http.max_content_length threshold (e.g., 50 MB). The elasticsearch‑py bulk helper supports max_chunk_bytes.
Before
helpers.bulk(es, actions, chunk_size=5000) # may create >100 MB chunks
After
helpers.bulk(
es,
actions,
chunk_size=2000, # fewer docs per chunk
max_chunk_bytes=50 * 1024 * 1024 # 50 MB limit per request
)
For Java or Logstash pipelines, set bulk_actions or bulk_size accordingly:
# Logstash output configuration
output {
elasticsearch {
hosts => ["es-prod.example.com:9200"]
bulk_actions => 2000 # default 500
bulk_size => "50mb" # default 20mb
flush_interval => "5s"
}
}
Server‑Side Tuning (Optional)
If the ingestion volume cannot be reduced (e.g., a one‑shot data lake load), increase the limit safely:
# Increase to 200 MB (requires a rolling restart)
PUT /_cluster/settings
{
"persistent": {
"http.max_content_length": "200mb"
}
}
Note: Raising the limit consumes more heap and network buffers; monitor jvm.heap_used_percent and network.inbound.bytes after the change.
For thread‑pool saturation, reduce parallel bulk workers in the CI job:
# Example: limit to 4 concurrent bulk processes
export BULK_CONCURRENCY=4
python ingest.py --workers $BULK_CONCURRENCY
Verification
- Re‑run the CI job with the updated client settings. Expected log snippet:
[2026-06-30T12:18:45,102][INFO ][o.e.b.BulkProcessor] [node-1] successfully indexed 2000 documents (45.2mb) in 1.2s - Confirm no 413 responses:
curl -s -o /dev/null -w "%{http_code}" -XPOST "https://es-prod.example.com:9200/_bulk" --data-binary @small_chunk.json # Should output 200 - Check bulk thread‑pool metrics:
curl -s "https://es-prod.example.com:9200/_cat/thread_pool/bulk?v" # queue column should stay well below the configured limit - Validate data integrity:
curl -s "https://es-prod.example.com:9200/my-index/_count" # Count should match the number of source documents
Prevention and Best Practices
- Adopt a safe bulk size range: 5 MB – 50 MB per request is recommended for most clusters (Bulk Processor docs).
- Make bulk size configurable per environment (e.g., CI vs. production) rather than hard‑coding.
- Monitor
http.total_requestsandthread_pool.bulk.rejectedin your observability stack; trigger alerts when thresholds exceed 80 % of the configured limits. - Implement exponential back‑off and retry for bulk calls that receive 429 or 413 responses.
- Keep
http.max_content_lengthat a sane default (100 mb) and only increase after load‑testing with realistic data volumes.
FAQ
- Why does the bulk request succeed locally but fail in the CI pipeline?
Local runs often use a smaller dataset or a higher
http.max_content_lengthon a dev cluster. CI pipelines typically aggregate the full training set, producing a payload that exceeds the production limit. - Can I increase
http.max_content_lengthwithout restarting the cluster?No. Changing this setting requires a full node restart because it is an HTTP server parameter.
- What is the relationship between
thread_pool.bulk.queue_sizeand bulk size errors?When many parallel bulk requests are queued, Elasticsearch may reject new requests with 429. This is separate from the 413 size limit but can appear together if the CI job spawns many workers.
- How do I calculate an appropriate
max_chunk_bytesvalue?Measure the average document size (e.g.,
curl .../_search?size=0&filter_path=hits.total) and multiply by the desired number of docs per chunk. Keep the total under 80 % ofhttp.max_content_lengthto allow overhead. - Is there a way to let Elasticsearch automatically split oversized bulk payloads?
The server does not split requests. The client must perform chunking. Use the official bulk helper libraries or implement custom chunking logic.
Related Topic Hub: Data Infrastructure Troubleshooting Hub