What 409 means
The request is valid, but it conflicts with the current state of the resource, so the server can't apply it. 409 is defined in RFC 9110 §15.5.10, which expects the conflict to be something the client can resolve and asks the server to explain it in the body:
HTTP/1.1 409 Conflict
Content-Type: application/problem+json
{
"type": "https://api.example.com/errors/duplicate-email",
"title": "Email already registered",
"detail": "An account with ana@example.com already exists."
}
The application/problem+json format (RFC 9457) is a good default for that body.
Common causes
- Duplicates: creating a user, slug or bucket whose unique value is already taken. S3 answers 409
BucketAlreadyExists. - Edit conflicts: two clients update the same record and the second one is working from a stale version. Kubernetes answers 409 when an update carries an old
resourceVersion. - Invalid state transitions: cancelling an order that has already shipped, deleting a non-empty bucket (
BucketNotEmpty), merging a branch that has conflicts (the GitHub API answers 409). - Retried creates: a POST that succeeded but timed out is sent again and now hits its own first copy.
How to fix it as a client
- Read the body. It should say which value or version conflicts.
- For version conflicts, fetch the latest copy, reapply your change on top of it, and send it again. Don't retry blindly: the same request will conflict again.
- For duplicates, treat "already exists" as success when that's what you wanted (an idempotent create), or ask the user for a different value.
- Make creates idempotent with a client-generated ID or an
Idempotency-Keyheader where the API supports it, so a retried POST doesn't conflict with itself.
How to send it as a server
Use 409 when the request is well-formed and valid on its own but clashes with stored state. Two neighbours fit better in specific cases:
- When the client sent a precondition such as
If-Match: "v3"and the ETag no longer matches, answer 412 Precondition Failed. That's the standard optimistic-locking flow. - When the data itself is invalid (a malformed email, a missing field), answer 422 Unprocessable Content or 400 Bad Request.
Map the database's unique-constraint error to 409 in one place instead of letting it surface as a 500.
409 and SEO
409 comes from APIs and form submissions, which search engines don't crawl, so it rarely matters for SEO. If a public page answers 409 to GET, Google treats it like any 4xx other than 429: the URL isn't indexed. Pages meant for search should answer 200.
Related codes
- 422 Unprocessable Content: the request's contents are invalid, regardless of state.
- 400 Bad Request: the request is malformed.
- 410 Gone: the resource you tried to change was deleted for good.
- 429 Too Many Requests: for retries that come too fast.
To reproduce a conflict, send the same request twice with the HTTP Request Builder and compare the responses.