409 Conflict
409 Conflict means the request could not be completed because it conflicts with the current state of the target resource, for example creating something that already exists or updating a record someone else just changed.
The request conflicts with the current state of the resource.
Typical cases: creating a record whose unique key already exists, updating a resource that someone else changed in the meantime, or a git-style push that is not a fast-forward. The body should explain the conflict so the client can resolve it and retry. Kubernetes returns 409 when you update an object with a stale resourceVersion.
Explained
The request is valid on its own; it fails because of what is already on the server. Typical cases: a signup with a username that is taken, a POST that violates a unique constraint, or an update based on an old version of a document.
For concurrent edits there is a more precise tool. If the client sends If-Match with the ETag it last saw, the server can reject a stale write with 412 Precondition Failed. 409 is the usual choice when the version check is in the body or the conflict is something other than a version mismatch.
Kubernetes is a well-known example: updating an object with an outdated resourceVersion returns 409, and controllers are expected to refetch and retry. Git hosting APIs return 409 for some non-fast-forward or merge-conflict cases.
How to debug it
- Read the response body; a good 409 says what it conflicts with.
- Fetch the current state of the resource and compare it with what you sent.
- If two clients or workers write the same resource, add version checks and a refetch-and-retry loop.
- For a unique-key conflict, decide whether the client should treat the existing record as success (an idempotent create) or as an error.
Gotchas
- Retrying a 409 unchanged will fail again; the client must change the request or refresh its data first.
- Do not use 409 for plain validation errors; those are 400 or 422.
Related explainers
- 422 Unprocessable Content vs 400 Bad Request400 means the request is malformed; 422 means it parsed fine but the data is invalid. When to use each for...
- 204 No Content204 means success with no response body. When APIs should return it, why res.json() throws on it, and how...
- 405 Method Not Allowed405 means the URL exists but rejects that HTTP method. Common causes: missing routes, trailing-slash...