Mistral API 400 Bad Request
Mistral validates the shape of the conversation itself, so a 400 here is frequently about the order of your messages rather than the contents of any one of them.
📡 Monitor your APIs — know when they go down before your users do
Better Stack checks uptime every 30 seconds with instant Slack, email & SMS alerts. Free tier available.
Affiliate link — we may earn a commission at no extra cost to you
Live Mistral status right now
Check this first only to close it off. A 400 is never an outage — a service that is down cannot read your payload well enough to object to it — but ruling the platform out in thirty seconds is cheaper than arguing about it for an hour.
A 400 is the only refusal that is genuinely about what you sent. The connection succeeded, TLS completed, the credential was accepted, your quota was never in question — and then Mistral read the body and could not use it. No tokens were generated. Nothing was billed. Nothing half-happened, which is the one mercy this error offers compared with a 500.
That makes it the most tractable error in the catalogue and the one most often debugged in the wrong place. Teams open the status page, check the billing dashboard and re-read the retry policy, when the entire fault surface is a few hundred bytes of JSON that are sitting in a variable one stack frame away. The response body names the offending field. Almost nobody reads it, because the SDK threw an exception before anyone got the chance.
30-second triage: serialise the exact body your client was about to send, write it to a file, and replay it with curl. The raw response will name the field Mistral objected to. If curl reproduces the 400, the bug is in how you built the request and no amount of retrying, failing over or checking live Mistral status will change it. If curl succeeds with the same bytes, your client is not sending what you think it is sending — look at middleware, a proxy, or a serialiser that drops or adds fields.
400, 401, 404, 422 and 429 are five different problems
These get collapsed into “the API rejected us” and handled by one branch, which is how a malformed body ends up in a retry loop and a validation bug ends up being escalated as an incident. The distinction is how far the request got before it stopped.
| 400 Bad Request | 401 Unauthorized | 404 Not Found | 422 Unprocessable | 429 Too Many Requests | |
|---|---|---|---|---|---|
| What was refused | The shape of what you sent | Who you are | The thing you named | The values you sent | How much you asked for |
| Was the body parsed? | Attempted, then rejected | Never looked at | Routed before parsing | Parsed successfully | Usually not reached |
| Does retrying help? | Never — deterministic | Never | Never | Never | Yes, after a backoff |
| Who fixes it | Whoever builds the request | Secret-store owner | Whoever pins model ids | Whoever owns the input data | Whoever controls pacing |
| Counts as Mistral downtime? | Never | Never | Never | Never | Never |
The row that costs the most time is the third. A client that applies one retry policy to every non-2xx response will hammer a deterministic refusal until its deadline expires, converting an error you could have shown in milliseconds into a request that hangs for the full retry budget and then times out — and a timeout is the one symptom that genuinely does look like an outage. That is how a malformed body gets escalated to an on-call engineer. The full map is in the Mistral API error codes reference.
Separate Your Bad Requests From Their Bad Days
External checks run from outside your infrastructure with their own known-good payload, so a 400 in your application is never debugged as a possible provider incident — you can see the provider answering a valid request perfectly well at the same moment.
Try Better Stack Free →Read the response body, not the SDK exception
A 400 is the error where the provider actually tells you the answer, and the SDK is the thing most likely to throw it away. Most clients collapse a validation failure into a generic exception with a message that has been through two layers of wrapping: the status code survives, and the JSON body naming the offending field frequently does not. Replay the call at the wire level before forming a theory:
# Write the exact body your client builds to a file, then replay it verbatim: curl -i -s -X POST https://api.mistral.ai/v1/chat/completions \ -H "Authorization: Bearer $MISTRAL_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @body.json # Prove the bytes are valid JSON before blaming the schema: python3 -m json.tool < body.json > /dev/null && echo "parses"
Those two commands split the category in half. If the file does not parse, you have a serialisation bug and the schema is irrelevant. If it parses and Mistral still objects, the response body names the field, and you are done. The structural fix is to log the outgoing body — redacted, and only on a 400 — so the next occurrence is diagnosed from a log line instead of a reproduction. See the Mistral API testing guide for wiring that into a contract test rather than discovering it in production.
The Mistral trap: the conversation is validated, and tool_call_id has a fixed length
Most providers accept whatever message array you hand them and let the model sort out the mess. Mistral validates it. The conversation has to terminate on a user or tool message (or an explicit assistant prefix), and roles may not repeat where alternation is expected — two assistant turns in a row is a 400 before generation begins. Nothing about that is arbitrary: an array that ends on an assistant turn is asking the model to continue its own sentence, which is usually a bug in the caller rather than an intent.
Agent loops hit this more than anything else, because the natural retry — append the model's last reply, then call again — produces exactly the forbidden shape. So does a conversation reconstructed from a database where a failed turn was persisted before its user follow-up. The tell is that the 400 rate correlates with conversation depth: single-turn calls are fine, and the failures cluster in long sessions. If your error dashboard can group by message count, the shape of that histogram is the diagnosis.
The second Mistral-specific rule catches every OpenAI-shaped harness exactly once: tool_call_id must be exactly nine alphanumeric characters. A client that mints UUIDs for tool call ids — the default in most agent frameworks, because it is the correct behaviour on other providers — sends back its first tool result and gets a 400. Echo the id Mistral issued rather than generating your own. Mistral also runs on three deployment planes (La Plateforme, cloud marketplaces and self-hosted), which validate at slightly different versions, so a body accepted in one is rejected in another and the difference surfaces at an environment boundary rather than in code review.
The six causes, in the order worth checking
Work down this list rather than across it. Each check is cheap, and the ordering reflects which answers turn out to be correct most often rather than which are most interesting.
- The message array ends on an assistant turn, or repeats a role. The classic agent-loop retry shape. Correlates with conversation depth, not with any one prompt.
- The JSON never parsed. A trailing comma, a
NaN, or an unescaped control character that arrived inside user-supplied text. The body looks fine in a log because the log printed the object, not the bytes. - A field has the right name and the wrong type.
max_tokensas a string,temperatureas a string, a boolean as"true". Anything that has been through a form, a query string or an environment variable is a suspect. - A content bound was exceeded.
max_tokensabove the model’s window, too many messages, an attachment over the size cap. These are input-dependent, so they fire for one request in a thousand and look intermittent. - A tool or function schema is not valid JSON Schema. Hand-written parameter definitions with a missing
typeor a malformedrequiredarray are rejected as part of the request, not at call time. - A null was sent where the field should have been omitted. Serialisers that emit every key make optional parameters explicit nulls, and an explicit null is a value the validator has to reject.
Notice the split running through that list. The first two are deploy-shaped: they appear at a release, affect a clean fraction of traffic, and are identical every time. The last three are input-shaped: they fire on one request in a thousand, look intermittent, and are usually triggered by something a user typed. Which of those two patterns your error rate shows is worth more than any individual hypothesis, and it is visible before you have read a single response body. If tool schemas are involved, the Mistral tool calling guide and the structured output guide cover the shapes that are accepted.
Make the next one impossible instead of fast
A 400 is a ten-minute fix that recurs forever unless the conditions that produced it change. Four structural changes remove most of the category permanently:
- Validate the body against a schema before it leaves your process. A local validator rejects the same request in microseconds, with a stack trace pointing at the line that built it, instead of a round trip and a field name. This is the single highest-leverage change and almost nobody makes it.
- Build requests in one place, per provider. Most deploy-shaped 400s come from a request-builder shared across providers whose parameter sets have quietly diverged. One builder per provider makes the divergence a compile-time fact rather than a runtime discovery.
- Log the outgoing body on a 400 only, redacted. The information needed to fix this error exists at the moment it happens and is discarded microseconds later. Capturing it turns every future occurrence from a reproduction exercise into a log lookup.
- Alert on 4xx separately from 5xx. A 400 rolled into a general error-rate alert either pages the wrong person or hides in noise. It has a different owner, a different runbook and a different urgency — and unlike a 5xx, it will not resolve itself. The setup is in the Mistral status alerts guide.
- Pin the SDK, and read the changelog on majors. Parameter sets and request shapes change between major versions, and a 400 arriving the day after a dependency bump is not a coincidence. The Mistral SDK version guide covers what moves.
Frequently Asked Questions
What does a 400 from the Mistral API actually mean?
It means the request reached Mistral, was parsed, and was found unusable on its contents. That is a narrower statement than it sounds, and the narrowness is the useful part: a 400 is decided after the credential was accepted and before any model work began, so it says nothing about your key, nothing about your quota, and nothing about capacity. Everything you would normally check when a call fails — the status page, the billing dashboard, your rate limiter — is on the wrong side of the point at which this one stopped. The entire fault surface is the bytes you sent.
Is a Mistral 400 ever caused by Mistral being down?
No, and the reasoning is worth keeping because it inverts the usual instinct. A 400 is a considered answer: something on the far end read your payload, understood it well enough to judge it, and refused. A service that is down cannot do that. So a 400 should never be counted against Mistral availability in your own metrics, and a monitor that treats every non-2xx response as downtime will page you for your own malformed request. The one edge case is a proxy, gateway or WAF in front of the API returning its own 400 — a header too large, a URL too long — which you can distinguish immediately because the body will not be Mistral's JSON error shape.
Should I retry a 400?
Not with the same body, ever. A 400 is deterministic: the identical request will be refused identically every time, so a backoff loop converts one instant refusal into dozens of them plus the full retry budget in latency, and delivers a timeout to your user instead of an error you could have shown them in milliseconds. If your client applies one policy to all non-2xx responses, this is the error that proves 4xx and 5xx need splitting. Fail fast, log the response body — which names the offending field — and surface it to an engineer rather than to a customer. A 400 is only retryable after the body has actually changed.
How do I tell a Mistral 400 from a 422?
By asking how far the request got before it was rejected. A 400 means the request could not be understood as a valid request at all: unparseable JSON, an unknown parameter, a field of the wrong type, a model id that is not a model id. A 422 means the request was understood perfectly and the values in it are not acceptable — well-formed, correctly typed, semantically wrong. The distinction matters because it tells you where to look. A 400 is usually a code or configuration bug you find by diffing your body against the schema; a 422 is usually a data bug you find by looking at the specific values that arrived. Providers are not always rigorous about which they return, so treat the response body as authoritative over the code.
Why does Mistral reject a message array that OpenAI accepts?
Because Mistral validates conversation structure and most providers do not. The array must end on a user or tool message, and roles must alternate where alternation is expected — two consecutive assistant turns is a 400 rather than a strange completion. The practical consequence is that porting an agent loop to Mistral surfaces bugs that were always present and previously silent: a retry that re-appends the model's own reply, or a conversation rebuilt from a table where a failed turn was written without its follow-up. Fix the loop rather than working around the validation, because the shape Mistral is rejecting was producing a degraded answer everywhere else.
What is the nine-character tool_call_id rule?
Mistral requires tool_call_id to be exactly nine alphanumeric characters, which is unusual and which breaks the default behaviour of nearly every OpenAI-shaped agent framework: those mint a UUID for each tool call, which is thirty-six characters with hyphens and therefore an immediate 400 on the first tool result returned. The fix is to echo back the id Mistral gave you in its tool_calls response rather than generating one, which is the correct behaviour on every provider and merely optional elsewhere. Because the failure only fires on the tool-result leg, a harness can pass every non-tool test and fail the moment function calling is switched on in production.
Related Mistral Guides
Stop Escalating Your Own Payload as a Mistral Outage
API Status Check watches Mistral and the rest of your stack from outside your infrastructure with its own known-good request — so a malformed body never gets escalated as an incident, and a real incident never gets dismissed as somebody’s bad JSON.
Start Your Free Trial →Alert Pro
14-day free trialStop checking — get alerted instantly
Alert Pro checks the 60+ APIs we monitor every hour and emails you within the hour of a detected change.
- Email alerts for up to 10 of the APIs we monitor
- $0 charged today — card required to start
- Cancel anytime — $9/mo after trial
🌐 Can't Access Mistral?
If Mistral is working for others but not for you, it might be an ISP or regional issue. A VPN can help bypass network-level blocks and routing problems.
Troubleshoot with a VPN
Connect from a different region to test if the issue is local to your network. Also protects your connection on public Wi-Fi.
Try NordVPN — 30-Day Money-Back GuaranteeSecure Your Mistral Account
Service outages are a common time for phishing attacks. Use a password manager to keep unique, strong passwords for every account.
Try NordPass — Free Password Manager⏳ While You Wait — Try These Alternatives
🛠 Tools We Use & Recommend
Tested across our own infrastructure monitoring 200+ APIs daily
SEO & Site Performance Monitoring
Used by 10M+ marketers
Track your site health, uptime, search rankings, and competitor movements from one dashboard.
“We use SEMrush to track how our API status pages rank and catch site health issues early.”