What does HTTP status 409 Conflict mean, and give concrete situations where it is the right answer rather than 400 or 422?
answer
- 409 = clash with current resource state
- Would the same request succeed later? → 409
- Duplicate key, lost update, illegal transition
- If-Match mismatch → 412; server demands it → 428
- Not blindly retryable; return current version to rebase
basics
~20 s409 means the request is well-formed and valid but conflicts with the current state of the target resource — a duplicate unique key, a lost-update on concurrent edits, or an illegal state transition. The body should explain the conflict so the client can resolve and resubmit.
solid answer
~60 s**409 Conflict** signals that the request could not be completed because of a conflict with the **current state of the target resource**. RFC 9110 adds that servers should include enough information for the user to recognise the conflict — 409 assumes the client might be able to fix it and retry. Canonical cases: - **Uniqueness violation**: registering an email or slug that already exists. - **Concurrent modification / lost update**: two clients editing the same record; the second is rejected because the version it based its edit on has moved on. - **Illegal state transition**: cancelling an order that already shipped, or deleting a non-empty container. It is *not* for malformed syntax (400) or field-level validation failures (422) — those are about the request itself, whereas 409 is about the world the request landed in. The same input might succeed a second later. When the client sent conditional headers such as `If-Match` and the validator no longer matches, **412 Precondition Failed** is the more precise code; 409 is the answer when the conflict is detected by application state rather than by a precondition the client declared.
code
http · 10 linesPUT /api/docs/7 HTTP/1.1
Content-Type: application/json
{"version":7,"title":"Q3 plan"}
HTTP/1.1 409 Conflict
Content-Type: application/json
{"code":"VERSION_CONFLICT","currentVersion":9,
"message":"Document was modified since version 7"}go deeper
Say 409 means the request clashes with the resource's current state, and give one example such as a duplicate email on signup.
Separate it cleanly from 400 and 422, add lost-update and illegal-transition examples, and note that the body should say what conflicted.
Bring in optimistic concurrency, 412 versus 409 versus 428, returning the current version so clients can rebase, retry semantics, and the enumeration risk on signup.
Discuss conflict policy across a platform: where concurrency control lives, which resources demand preconditions, and how idempotent creates keep client retries from surfacing as false conflicts.
## The core idea: state, not syntax Most 4xx codes describe something wrong with the *request*: unparseable (400), semantically invalid (422), wrong verb (405), unauthenticated (401). **409 Conflict** is different — the request is perfectly good, but it clashes with the **current state of the target resource**. RFC 9110 frames 409 as a situation the user might resolve and resubmit, and requires enough information in the response for them to recognise the source of the conflict. A useful test: *would this exact request succeed at some other point in time?* If yes, it is a state problem — 409 territory. If it could never succeed regardless of state, it is 400/422. ## Case 1: uniqueness violations Creating a user with an email that is already registered is the textbook 409. The body is valid, the caller is authorised, and the same request would have succeeded yesterday. 422 is sometimes used here ("the email field is invalid") and is not indefensible, but it hides the real cause: the value is fine, the world changed. Beware the information-disclosure angle: on public signup, replying 409 "email already registered" is an account-enumeration oracle. Many products deliberately return a generic 202/200 and send an email instead. That tradeoff — precision versus enumeration — is a good senior-level thing to raise. ## Case 2: concurrent modification Two editors load version 7 of a document; both submit changes. The first wins; the second must not silently overwrite (the *lost update* problem). How the conflict is detected determines the code: - **Client declared a precondition.** The client sends `If-Match: "v7"`. The current entity-tag is now `"v8"`, so the server answers **412 Precondition Failed**. This is the sharper, protocol-level answer, and it works for any resource with a validator. - **Application-level version check.** The payload carries a `version` field, or the server compares an internal revision. When the mismatch is discovered in business logic, **409** with a body explaining the conflicting revision is the natural answer. Related: **428 Precondition Required** is the code for "you must send `If-Match`", used when a server refuses to accept unconditional writes precisely to prevent lost updates. ## Case 3: illegal state transitions and referential conflicts - Cancelling an order that has already shipped. - Approving a request that was already withdrawn. - Deleting a project that still contains resources (delete would break references). - Starting a job that is already running for the same key. All of these are valid requests against a state that forbids them. 409 with a machine-readable code (`ORDER_ALREADY_SHIPPED`, `CONTAINER_NOT_EMPTY`) lets the client render the right message or take a corrective step. A blanket 400 forces clients to string-match human messages. ## What the response must carry - A **stable error code** the client can switch on. - **Enough state information to resolve it**: the current version/ETag, the conflicting field, or the identifier of the blocking child resource. For a version conflict, returning the current representation (or its ETag) lets the client rebase and retry in one round trip instead of a re-fetch. - Optionally a hint about whether an automatic retry can help (usually not — a human or a merge step is needed). ## Retry semantics 409 is generally **not blindly retryable**: the same request will conflict again until either the state changes or the client rebases. This distinguishes it from 429 and 503, which are explicitly "come back later". Clients that treat every 4xx/5xx alike and hammer a 409 endpoint are a common source of noisy traffic. A subtle case is a duplicate-submission retry: a client retries a create because it never saw the first response, and the second attempt hits a unique constraint and returns 409 even though the operation *succeeded*. That is why creates that clients may retry should be made idempotent — the retried create should return the original result, not a conflict. ## Distinguishing the neighbours quickly - **400** — malformed request, unparseable. - **422** — parsed, but the values are semantically invalid on their own terms. - **409** — values are fine; the current resource state forbids the operation. - **412** — a precondition the client explicitly sent (`If-Match`, `If-Unmodified-Since`) evaluated false. - **428** — the server demands a precondition the client did not send. - **423 Locked** (WebDAV) — the resource is locked by another party. Being able to lay out that ladder is what an interviewer is usually listening for.
- A client sends If-Match with a stale entity-tag on an update. Is 409 or 412 the correct status?412 Precondition Failed. The client explicitly declared a condition and the server evaluated it as false, which is exactly what 412 reports; the client knows to re-fetch, rebase, and retry with the fresh validator. Reserve 409 for conflicts your application logic discovers without a client-declared precondition.
- On a public signup form, is 409 "email already registered" a good response?It is semantically precise but creates an account-enumeration oracle: anyone can probe addresses and learn who has an account. Many products deliberately return a uniform success-shaped response and resolve the situation over email instead. It is a security-versus-usability tradeoff, and the right call depends on whether the existence of an account is sensitive.
- Should clients retry a 409 automatically?Generally no. Unlike 429 or 503, a 409 will keep failing until either the resource state changes or the client rebases its request on the current state. Automatic retries just add load. The exception is an optimistic-concurrency loop that re-reads the current version, re-applies the change, and resubmits — a bounded number of times.
saying these in an interview costs you the question
- Using 409 for ordinary field validation failures (that is 422 or 400)
- Returning 409 for a stale If-Match instead of 412
- Returning a bare 409 with no indication of what conflicted or the current version
- Assuming clients should retry 409 with backoff like a 429
- Treating a duplicate create caused by a client retry as a genuine conflict instead of making the create idempotent