409 Conflict
The request conflicts with the current state of the resource.
What 409 means
409 covers the case where the request is well-formed and permitted but cannot be applied because of the resource's current state. Creating something that already exists, updating a record that changed since you read it, deleting a resource other things still depend on - these are conflicts rather than validation failures.
Its most valuable use is optimistic concurrency control. The client sends If-Match with the ETag it last saw; if the resource has changed, the server returns 409 (or 412) rather than silently overwriting someone else's update. That is the standard fix for the lost-update problem, and it needs no locking.
Because 409 covers several situations, the response body has to say which. A bare 409 leaves the client unable to decide between 'reload and retry', 'the name is taken, ask the user for another' and 'remove the dependents first'.
Common causes of a 409
- A uniqueness violation - the username, email or slug already exists.
- A concurrent update: the resource changed since the client read it.
- Attempting to delete a resource that other records still reference.
- A state machine violation, such as cancelling an order that has already shipped.
- Creating a resource whose identifier is already in use.
How to fix a 409
- Say in the body which conflict occurred and what the client should do about it.
- For concurrent updates, support If-Match with ETags so the client can retry against fresh state.
- For uniqueness, return the conflicting field name so the UI can highlight it.
- Consider whether the operation could be made idempotent instead, so a repeat is harmless.
Should a client retry?
Retry only after resolving the conflict - re-reading the resource, or changing the conflicting value. An immediate identical retry will conflict again.
FAQ
- 409 or 422?
- 422 when the request's own content is invalid in isolation. 409 when the content is fine but conflicts with existing state - a duplicate email is a conflict, an email with no @ is a validation failure.
- 409 or 412 for a failed conditional update?
- 412 Precondition Failed is the more precise answer when an If-Match or If-Unmodified-Since condition failed. 409 is acceptable and widely used, but 412 tells the client specifically that its precondition was the problem.
- How do I prevent lost updates?
- Return an ETag on reads and require If-Match on writes. If the resource changed since the client read it, the ETag will not match and you return 409 or 412 instead of overwriting the other update.
Often confused with
- 412 Precondition Failed A conditional request's precondition was not met, so the request was not applied.
- 422 Unprocessable Entity The request was well-formed but failed validation rules.