Cohere API 400 Bad Request

Cohere ships two chat request shapes under one brand, and sending v2’s body to v1’s endpoint is a 400 whose message reads like a typo.

11 min read
Staff Pick

📡 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.

Start Free →

Affiliate link — we may earn a commission at no extra cost to you

Live Cohere 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 Cohere 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 Cohere 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 Cohere 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 Request401 Unauthorized404 Not Found422 Unprocessable429 Too Many Requests
What was refusedThe shape of what you sentWho you areThe thing you namedThe values you sentHow much you asked for
Was the body parsed?Attempted, then rejectedNever looked atRouted before parsingParsed successfullyUsually not reached
Does retrying help?Never — deterministicNeverNeverNeverYes, after a backoff
Who fixes itWhoever builds the requestSecret-store ownerWhoever pins model idsWhoever owns the input dataWhoever controls pacing
Counts as Cohere downtime?NeverNeverNeverNeverNever

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 Cohere API error codes reference.

📡
Recommended

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.cohere.com/v2/chat \
  -H "Authorization: Bearer $COHERE_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 Cohere 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 Cohere API testing guide for wiring that into a contract test rather than discovering it in production.

The Cohere trap: v1 and v2 are two request shapes on one brand

Cohere's v1 chat takes a single message string plus a chat_history array. Its v2 chat takes an OpenAI-style messages array. Both are current, both are documented, and sending one shape to the other endpoint produces a 400 whose field-level complaint reads exactly like a typo — a missing required field, an unexpected property — which sends people hunting for a misspelling in a body that is entirely well-formed for the other version.

What makes this durable rather than a one-time mistake is that the ecosystem is split across both. Tutorials, blog posts, Stack Overflow answers and two SDK major versions all exist for each shape, and none of them are wrong; they are answers to different questions that look identical out of context. The wrong shape is one copy-paste away at all times, and it is most likely to arrive in the least-reviewed code: a quick script, a migration, a fix applied under time pressure. Pin the version explicitly in the URL and in the SDK dependency, and put the version in the client's name so a call site tells you which contract it is speaking.

The second Cohere rule is more subtle because the same field behaves differently on two paths. Embedding with a v3 model requires input_type, and omitting it is a hard 400 — but supplying the wrong one (search_document where you meant search_query) is not an error at all. It is a silently degraded retrieval that your evaluation set may not be sensitive enough to catch. The error you can see and the bug you cannot are the same parameter, so treat it as a first-class part of your indexing contract rather than a flag on a call. Chat, embed and rerank are three separate surfaces with separate validation, which means a RAG pipeline can 400 on a leg your logs do not name.

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.

  • A v2-shaped body was sent to the v1 endpoint, or the reverse. messages versus message plus chat_history. The error reads like a typo and is not.
  • 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_tokens as a string, temperature as 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_tokens above 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 type or a malformed required array 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 Cohere 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 Cohere 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 Cohere SDK version guide covers what moves.

Frequently Asked Questions

What does a 400 from the Cohere API actually mean?

It means the request reached Cohere, 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 Cohere 400 ever caused by Cohere 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 Cohere 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 Cohere'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 Cohere 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 Cohere say a required field is missing when my body looks correct?

Because it is correct — for the other API version. Cohere v1 chat takes a message string plus chat_history; v2 chat takes a messages array. Send a v2 body to v1 and the endpoint reports message as missing and messages as unexpected, which reads precisely like a spelling mistake in a body that has no spelling mistake in it. Both versions are live and documented, and the ecosystem of tutorials and SDK majors is split across them, so the wrong shape is permanently one copy-paste away. Pin the version in the URL and in the dependency, and name the client for the version it speaks so a call site is self-documenting.

Why does Cohere embed return a 400 about input_type?

Because v3 embedding models require it: you must declare whether the text is a document being indexed (search_document) or a query being searched (search_query), and omitting it is rejected outright. Worth knowing is the asymmetry — omitting the field is a loud 400 you will fix in minutes, while supplying the wrong value is not an error at all and quietly degrades retrieval quality in a way a small evaluation set will not detect. That makes input_type part of your indexing contract rather than a per-call flag: set it once where documents and queries are distinguished in your own code, and the noisy failure and the silent one are both closed by the same change.

Related Cohere Guides

Stop Escalating Your Own Payload as a Cohere Outage

API Status Check watches Cohere 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 trial

Stop 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 Cohere?

If Cohere 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 Guarantee
🔑

Secure Your Cohere 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
Quick ISP test: Try accessing Cohere on mobile data (Wi-Fi off). If it works, the issue is with your ISP or local network.

⏳ While You Wait — Try These Alternatives

🛠 Tools We Use & Recommend

Tested across our own infrastructure monitoring 200+ APIs daily

SEMrushBest for SEO

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.

From $129.95/moTry SEMrush Free
View full comparison & more tools →Affiliate links — we earn a commission at no extra cost to you