Problem
During a production rollout of a real‑time inference service, the MLflow tracking server started rejecting mlflow.log_metric and mlflow.log_artifact calls originating from a high‑frequency streaming pipeline. The client observed the following error in the pipeline logs:
2026-09-28 14:12:03,721 ERROR mlflow.tracking.context – Context window overflow: attempted to write 128 KB into a 64 KB window (request_id=7f3c9a2e‑b1d4‑4e5a‑a2c1‑d9f1e2c3b6e7)
Symptoms include:
- Intermittent
HTTP 413 Request Entity Too Largeresponses from the tracking server. - Missing inference request payloads in the MLflow run history.
- Increased latency in the streaming pipeline due to retry back‑off.
Root Cause
The MLflow tracking server uses a gRPC‑based TrackingService with a configurable max_message_length (default 64 KB). The streaming pipeline batches inference request payloads (JSON blobs) and logs them as a single artifact per inference batch. When the batch size exceeds the gRPC message limit, the server aborts the RPC with a RESOURCE_EXHAUSTED status, which the client surface as a “context window overflow”. The overflow is not a database issue; it is a transport‑layer limitation triggered by:
- High request rate (≈10 k inferences / second).
- Payloads averaging 8 KB each, aggregated into batches of 16–20 items.
- Default gRPC limits unchanged from the MLflow reference deployment.
Because the pipeline only logs every 100 ms, the batch window grows until the gRPC frame size is exceeded, causing the overflow.
Debug
Step‑by‑step investigation:
- Inspect MLflow server logs for the exact error code.
2026-09-28 14:12:03,720 INFO mlflow.tracking.server - Received LogArtifact request (run_id=3a5c9d, size=131072)
2026-09-28 14:12:03,721 ERROR mlflow.tracking.context - Context window overflow: attempted to write 128 KB into a 64 KB window
2026-09-28 14:12:03,721 WARN grpc.server - RPC failed: RESOURCE_EXHAUSTED: Message size exceeds limit
- Capture the outbound gRPC payload size using
grpcurlor a packet capture.
# grpcurl -plaintext localhost:5000 mlflow.TrackingService/LogArtifact -d @ <<EOF
{
"run_id": "3a5c9d",
"key": "inference_batch_20260928_141200",
"value": "... (base64 payload) ..."
}
EOF
# Expected: 64KB limit, Actual: 128KB
- Verify batch size in the streaming application. The pipeline uses a
Batcherclass that accumulates payloads untilbatch.max_itemsorbatch.max_interval_msis reached.
class Batcher:
def __init__(self, max_items=20, max_interval_ms=100):
self.max_items = max_items
self.max_interval_ms = max_interval_ms
self.buffer = []
self.last_flush = time.time()
def add(self, payload):
self.buffer.append(payload)
if len(self.buffer) >= self.max_items or (time.time() - self.last_flush) * 1000 >= self.max_interval_ms:
self.flush()
With an average payload of 8 KB, max_items=20 yields ~160 KB per batch, exceeding the limit.
Solution
Two complementary changes resolve the overflow:
1. Reduce the gRPC message size limit on the MLflow server
Adjust the server’s MLFLOW_TRACKING_MAX_MESSAGE_SIZE environment variable (or --max-message-size CLI flag) to accommodate the largest expected batch.
Before:
# docker run -d -p 5000:5000 mlflow/mlflow:2.12.0
After:
# docker run -d -p 5000:5000 \
-e MLFLOW_TRACKING_MAX_MESSAGE_SIZE=262144 \
mlflow/mlflow:2.12.0
This raises the limit to 256 KB, enough for a batch of up to 30 KB payloads.
2. Tune the streaming batcher to stay within the limit
Modify the batcher configuration to either shrink max_items or split large payloads into separate artifacts.
Before:
batcher = Batcher(max_items=20, max_interval_ms=100)
After (option A – smaller batch):
batcher = Batcher(max_items=8, max_interval_ms=100) # ~64KB max
After (option B – payload chunking):
def split_payload(payload, chunk_size=65536):
return [payload[i:i+chunk_size] for i in range(0, len(payload), chunk_size)]
def add(self, payload):
for chunk in split_payload(payload):
self.buffer.append(chunk)
self.flush_if_needed()
Option A reduces latency; Option B preserves batch size while guaranteeing each artifact fits the gRPC frame.
Verify
Confirm the fix with the following steps:
- Restart the MLflow server with the new
MLFLOW_TRACKING_MAX_MESSAGE_SIZEvalue. - Deploy the updated streaming application.
- Trigger a high‑frequency load test (e.g.,
hey -c 200 -n 20000 http://inference-service/predict). - Check MLflow run logs for successful artifact entries.
2026-09-28 14:45:12,003 INFO mlflow.tracking.server - LogArtifact succeeded (run_id=3a5c9d, key=inference_batch_20260928_144500, size=58KB)
2026-09-28 14:45:12,004 INFO mlflow.tracking.server - LogArtifact succeeded (run_id=3a5c9d, key=inference_batch_20260928_144600, size=62KB)
Metrics to monitor:
- MLflow server
grpc_server_message_size_exceeded_totalcounter should stay at zero. - Streaming pipeline latency < 150 ms per batch.
- No
HTTP 413responses innginx/access.logormlflow_server.log.
Prevent
Implement guardrails to avoid recurrence:
- Dynamic batch sizing: calculate batch size based on
MLFLOW_TRACKING_MAX_MESSAGE_SIZEretrieved from the server at startup. - Metric‑driven alerts: create a Prometheus alert on
grpc_server_message_size_exceeded_total > 0over a 5‑minute window. - Schema validation: enforce a maximum payload size (e.g., 8 KB) at the inference service boundary.
- CI test: add an integration test that sends a batch of
max_itemspayloads and asserts successful logging.
Related Topic Hub: Model Serving Troubleshooting Hub
FAQ
- Why does increasing
MLFLOW_TRACKING_MAX_MESSAGE_SIZEnot always fix the overflow?If the client continues to batch payloads larger than the new limit, the server will still reject them. Both server limits and client batching must be aligned.
- Can I disable the gRPC limit entirely?
Setting the limit to
0removes the cap, but it is unsafe: a malformed or malicious client could exhaust server memory, leading to denial‑of‑service. - Is there a way to stream large payloads without using artifacts?
Yes. Use
mlflow.log_textfor line‑by‑line logging or store raw payloads in an external object store (S3, GCS) and log only the reference URI in MLflow. - How do I determine the optimal batch size for my workload?
Measure the average payload size, add a safety margin (≈20 %), and compute
max_items = floor(MLFLOW_TRACKING_MAX_MESSAGE_SIZE / avg_payload_size). Adjust dynamically if payload distribution changes. - Will the same issue appear when using the REST API instead of gRPC?
The REST endpoint enforces a similar
max_content_length(default 64 KB). The fix is analogous: increase the server’sMLFLOW_MAX_REQUEST_SIZEconfiguration and/or shrink client batches.