MLflow Tracking — 400 Bad Request After JSON Schema Validation Failure
Problem
When logging experiment metadata (e.g., mlflow.log_param, mlflow.log_metric, or mlflow.set_tag) to a managed MLflow tracking endpoint (Azure ML, AWS SageMaker, GCP AI Platform), the client receives:
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error_code": "INVALID_PARAMETER_VALUE",
"message": "JSON schema validation failed: request payload does not conform to the expected schema"
}
The failure is intermittent at first glance, but persists for a specific set of runs. No authentication errors appear, and the endpoint URL is reachable (HTTPS 200 on health‑check).
Root Cause
MLflow’s tracking server validates every incoming JSON payload against a strict OpenAPI schema. The schema enforces:
- String length limits (e.g.,
run_name≤ 255 characters) - Allowed characters for keys (ASCII alphanumerics,
_,-) - Data‑type constraints (numeric values must be JSON numbers, not strings)
- Maximum number of tags/parameters per request (default 100)
In the observed failures, one of the following violations occurred:
- Oversized tag value: a tag containing a serialized JSON object (> 500 bytes) exceeded the
maxLengthof 500 characters defined fortag.value. - Non‑ASCII characters in keys: experiment names generated from user IDs included Unicode emojis, which the schema rejects because
^[A-Za-z0-9_-]+$is enforced. - Numeric values sent as strings: a custom logging wrapper called
log_metric_floatperformedstr(value)before passing tomlflow.log_metric, causing the payload fieldvalueto be a JSON string instead of a number.
Because the tracking server aborts the request before persisting any data, the client sees only the generic 400 response without a stack trace.
Debug & Investigation
Step‑by‑step reproduction of the failure:
- Enable
MLFLOW_TRACKING_DEBUGon the client to capture request/response payloads. - Inspect the failing request body in the debug logs.
- Validate the JSON against the official schema using
jsonschemalocally.
Example debug session:
export MLFLOW_TRACKING_DEBUG=1
python train.py
...
2026-10-31 12:45:02,317 - mlflow.tracking.client - DEBUG - POST /api/2.0/mlflow/runs/log-metric
Payload:
{
"run_id": "12345abcde",
"key": "accuracy",
"value": "0.9234",
"timestamp": 1698765902000,
"step": 1
}
2026-10-31 12:45:02,322 - mlflow.tracking.client - ERROR - Request failed (status=400)
Response:
{
"error_code": "INVALID_PARAMETER_VALUE",
"message": "JSON schema validation failed: 'value' is not of type 'number'"
}
Validating the payload locally:
$ python - <<'PY'
import json, jsonschema, pathlib
schema = json.load(open('mlflow-openapi-schema.json'))['components']['schemas']['LogMetricRequest']
payload = {
"run_id": "12345abcde",
"key": "accuracy",
"value": "0.9234", # <-- string, should be number
"timestamp": 1698765902000,
"step": 1
}
try:
jsonschema.validate(payload, schema)
except jsonschema.ValidationError as e:
print(e)
PY
'value' is not of type 'number'
Similar steps revealed the oversized tag:
2026-10-31 13:02:11,104 - mlflow.tracking.client - DEBUG - POST /api/2.0/mlflow/runs/set-tag
Payload:
{
"run_id": "67890fghij",
"key": "model_config",
"value": "{\"layers\":[{\"type\":\"dense\",\"units\":1024}, ... 800 more chars ...}"
}
Response:
{
"error_code": "INVALID_PARAMETER_VALUE",
"message": "JSON schema validation failed: 'value' exceeds maximum length of 500"
}
Solution
Fixes are applied at three levels: client code, logging wrappers, and optional server configuration (if the managed service permits schema overrides).
1. Ensure numeric metrics are passed as numbers
Replace any str() conversion before mlflow.log_metric:
Before:
def log_metric_float(run_id, key, value, step=0):
# Incorrect: value becomes a string
mlflow.log_metric(key, str(value), step=step)
After:
def log_metric_float(run_id, key, value, step=0):
# Correct: keep as float
mlflow.log_metric(key, float(value), step=step)
2. Truncate or compress large tag values
For tags that need to store structured data, serialize to a compact representation (e.g., base64‑encoded gzip) and enforce the 500‑character limit.
Before:
mlflow.set_tag("model_config", json.dumps(config_dict))
After:
import json, gzip, base64
def compress_tag(value: dict) -> str:
json_bytes = json.dumps(value).encode('utf-8')
gz = gzip.compress(json_bytes)
return base64.b64encode(gz).decode('ascii')[:500] # enforce limit
mlflow.set_tag("model_config", compress_tag(config_dict))
3. Sanitize experiment/run names and tag keys
Strip or replace non‑ASCII characters before calling MLflow APIs:
import re
def sanitize_key(key: str) -> str:
# Keep only alphanumerics, underscore, hyphen
return re.sub(r'[^A-Za-z0-9_-]', '_', key)
run_name = sanitize_key(user_provided_name)
mlflow.start_run(run_name=run_name)
4. (Optional) Increase server limits
If the managed service exposes a configuration knob (e.g., mlflow.tracking.max_tag_length), raise it after confirming compliance with organizational policies. This step varies by provider and may require a support ticket.
Verification
After applying the fixes, repeat the logging steps and confirm that the server returns HTTP 200.
2026-10-31 14:10:05,212 - mlflow.tracking.client - DEBUG - POST /api/2.0/mlflow/runs/log-metric
Payload:
{
"run_id": "12345abcde",
"key": "accuracy",
"value": 0.9234,
"timestamp": 1698765902000,
"step": 1
}
2026-10-31 14:10:05,218 - mlflow.tracking.client - INFO - Metric logged successfully
Additional validation steps:
- Run
mlflow experiments listand verify the new run appears. - Query the tracking server via REST:
curl -s -H "Authorization: Bearer $TOKEN" $MLFLOW_ENDPOINT/api/2.0/mlflow/runs/get?run_id=12345abcdeand inspect thetagsandmetricsfields. - Check monitoring dashboards for a drop in 400‑error rate.
Prevention
Implement guardrails to catch schema violations before they reach the server:
| Guardrail | Implementation |
|---|---|
| Schema‑aware client wrapper | Wrap MLflow calls in a helper that runs jsonschema.validate against the local schema file. |
| Input sanitization library | Centralize sanitize_key and compress_tag functions in a shared utils module. |
| CI linting for experiment metadata | Add a unit test that generates a synthetic run with max‑size tags/parameters and asserts validation passes. |
| Alert on 4xx spikes | Configure monitoring (Prometheus/CloudWatch) to fire an alert when mlflow_http_4xx_total exceeds a threshold. |
Document the accepted schema limits in the engineering handbook and include them in code review checklists.
FAQ
- Why does the error only appear for some runs?
The offending payload fields (e.g., a long tag or a Unicode experiment name) are generated conditionally based on user input or model configuration. Runs that do not hit those code paths produce compliant JSON, so the server responds with 200. - Can I disable JSON schema validation on the managed tracking server?
Managed MLflow services typically enforce the schema for data integrity. Disabling it is not supported; the recommended approach is to fix the client payload. - How do I find the exact schema version used by the service?
Query the OpenAPI spec:curl $MLFLOW_ENDPOINT/api/2.0/openapi.json. Thecomponents.schemassection contains the definitions forLogMetricRequest,SetTagRequest, etc. - Is there a way to automatically truncate tag values?
Yes. Implement a wrapper aroundmlflow.set_tagthat checkslen(value)and truncates or compresses the string before sending. - What monitoring metric should I watch to detect future schema violations?
Trackmlflow_http_4xx_total{status="400"}(or the provider‑specific equivalent). A sudden increase indicates new payload violations.
Related Topic Hub: Model Serving Troubleshooting Hub