409 Conflict

A 409 Conflict is the server refusing to let you overwrite reality. The request was well-formed, every value passed validation, and the endpoint understood exactly what you wanted. It said no because the resource is not in the state your request assumed. Something already exists, or something changed while you were not looking.

Updated August 2026 · 7 min read

  • Written by

    Andrian Valeanu Andrian Valeanu Founder of Pulsetic

    Andrian Valeanu founded Pulsetic and, before it, Designmodo. Across 15-plus years he has shipped web products, design tools, and monitoring software teams around the world rely on.

  • Reviewed by

    Ionut Caval Ionut Caval Technical reviewer

    Ionut Caval reviews Pulsetic's technical guides for accuracy. He works hands-on with web servers, networking, and uptime monitoring day to day, and makes sure the causes and fixes here hold up in production.

The short version: Re-read the current state of the resource, reconcile your change against what is actually there, and resend. Do not retry the identical request, because the conflict will still be there. For updates, carry an ETag in an If-Match header so the server can tell you precisely when your copy has gone stale.

Key takeaways

  • 409 Conflict means the request is valid in itself but clashes with the current state of the resource. Nothing is malformed and nothing failed validation. The problem is what already exists on the server.
  • The clearest test against a 422 is whether the same request would succeed against an empty database. If it would, the failure is about state, and 409 is the right code.
  • Two situations produce most 409s: creating something whose unique value is already taken, and updating a resource that changed after you read it, which is the lost-update problem.
  • RFC 9110 expects the response to carry enough information for the client to recognise the source of the conflict, so a bare 409 with no body leaves the caller with nothing to act on.
  • Retrying unchanged rarely helps. The correct pattern is to re-read the current state, reconcile your change against it, and resend, ideally with a fresh ETag in an If-Match header.
Error type
HTTP 4xx client error
Whose side
Either: the request, or concurrent activity on the server
Fix difficulty
Moderate
Common cause
The request clashes with the resource's current state

How did you find out this time?

Pulsetic is website uptime monitoring. It checks your URL from outside your network as often as every 30 seconds, confirms any failure from another region, then emails you.

10 monitors free, email alerts on failure and recovery. No credit card.

What does 409 Conflict mean?

A 409 Conflict is an HTTP status code returned when a request cannot be completed because it clashes with the current state of the target resource. RFC 9110 describes it as a conflict the user might be able to resolve and resubmit, and it expects the response to contain enough information for the client to recognise the source. That framing is important, because it separates 409 from the other 4xx codes. The request was not malformed, so it is not a 400. Its values did not break a validation rule, so it is not a 422. Everything about it was acceptable in isolation, and it failed only when measured against what already exists on the server.

Two families cover almost every real case. The first is a uniqueness clash on creation: registering an email address that is already taken, or creating a resource with a slug that another record holds. Nothing is wrong with the value, it is simply spoken for. The second is a version clash on update, the classic lost-update problem, where you read a resource, someone else changed it, and your write would silently discard their change. Kubernetes is the most visible example, rejecting any update carrying a stale resourceVersion so that controllers re-read and reapply rather than overwrite. In both families the 409 is the server protecting data it would otherwise lose.

YouDNSNetworkCDN / ProxyWeb serverApp / DB
The path a request takes from your browser to the website's servers. A 409 Conflict is produced at the highlighted stages.
409
HTTP status code
4xx
Client-side error class
ETag
Header that prevents lost updates

How the 409 Conflict error appears

The wording changes depending on your browser, device, or server. Here is how this error commonly shows up:

What a 409 Conflict looks like in the browser. The exact wording varies by browser, device, and server.
  • 409 Conflict
  • HTTP Error 409
  • Error 409
  • HTTP 409
  • 409 Conflict API
  • the request could not be completed due to a conflict

409 vs 422, 412 and 400

These four reject requests that all look reasonable, and they differ in what the server actually checked.

Code What it means How to recover
409 Conflict The request is valid but clashes with the resource's current state, such as a value already taken or a version that moved on. Re-read the current state, reconcile the change, and resend.
422 Unprocessable Content The request parsed and was understood, but a value broke a validation or business rule regardless of state. Correct the offending values named in the response body.
412 Precondition Failed A precondition you supplied, typically If-Match with an ETag, no longer holds. Fetch the current representation and its fresh ETag, then retry.
400 Bad Request The request could not be parsed at all, so nothing further was checked. Fix the structure of the request rather than its values.

Conflict, validation, and precondition failures compared

These codes are easy to confuse because all four reject a request that looks reasonable, but they differ on what was actually checked.

CodeWhat was checkedHow the client recovers
409 ConflictCurrent state, checked by the server unpromptedRe-read the state, reconcile, resend
422 Unprocessable ContentThe values in the request, against validation rulesCorrect the offending values
412 Precondition FailedA precondition the client supplied, such as If-MatchFetch a fresh ETag and retry
400 Bad RequestSyntax, before anything elseFix the structure of the request

What causes 409 Conflict?

  • Creating a resource whose unique value already exists: an email address, a username, a slug, or any column with a uniqueness constraint behind it.
  • Updating a resource that changed after you read it, so your write would discard someone else's change. This is the lost-update problem, and 409 is how a server refuses to participate in it.
  • An optimistic-concurrency check failing, such as a Kubernetes update carrying a resourceVersion that is no longer current, or an ORM version column that has moved on.
  • A client retrying a create request after a timeout without an idempotency key, so the server cannot distinguish the retry from a genuine second attempt.
  • A state transition the resource does not permit from where it currently is, such as cancelling an order that has already shipped.
  • Two requests racing each other, where both read the same state and both try to act on it, and the second one loses.
  • A deletion or move that conflicts with a dependency, such as removing a record another resource still references.

How to find the cause fast

  1. Read the response body first. A well-built API names the conflicting field or resource, which usually distinguishes a uniqueness clash from a version clash immediately.
  2. Ask whether the same request would succeed against an empty database. If it would, the problem is state rather than validation, which confirms 409 rather than 422 is the right reading.
  3. Fetch the resource again and compare it with the copy your request was based on. A difference points at a concurrent write; no difference points at a uniqueness constraint.
  4. Check for a retry loop in the client. A sudden spike of identical 409s on a previously quiet endpoint is far more often a loop than a data problem.
What a 409 Conflict looks like from the command line. The grey lines starting with # are explanatory comments.

How 409 Conflict looks from the outside

A 409 is a normal HTTP response, so the service looks healthy from outside: the connection opens, TLS completes, and a well-formed answer comes back quickly. An up-or-down check will report the API up while writes are being refused. What makes it easy to miss is that a low background rate of 409s is entirely legitimate, since duplicate submissions and concurrent edits happen in any busy system. The failure worth catching is a change in that rate, particularly after a deployment that alters uniqueness constraints or concurrency handling, which needs a check that exercises the endpoint and asserts the status code it expects.

To confirm the exact code a URL returns, or to re-test several at once after a fix, run them through the free bulk URL status checker.

How to fix 409 Conflict

If you are calling an API

  1. Read the response body to find out what actually conflicted, then act on that rather than retrying blindly.
  2. For a duplicate, fetch the existing resource instead of creating a new one, or present the clash to the user so they can choose a different value.
  3. For an update, re-read the resource, merge your change into the current version, and resend. Do not simply resubmit your stale copy.
  4. Send an If-Match header with the ETag you read, so the server can reject a stale write precisely rather than guessing.
  5. Use an idempotency key on create requests, so a retry after a timeout returns the original result instead of colliding with it.
  6. Add backoff and a retry ceiling. A 409 that persists after a few reconciled attempts is a data problem, not a transient one, and looping will not resolve it.

If you run the API

  1. Return a body that names the conflict. RFC 9110 expects the response to let the client recognise the source, and a bare 409 leaves callers with nothing to work with.
  2. Keep 409 and 422 distinct: 409 for clashes with existing state, 422 for values that fail validation. Collapsing them makes both harder for clients to handle.
  3. Expose ETag on resources that can be edited concurrently, and honour If-Match, so clients can detect staleness instead of overwriting each other.
  4. Support idempotency keys on create endpoints, which removes the most common source of duplicate-creation conflicts entirely.
  5. Make sure a uniqueness clash returns 409 rather than surfacing as a 500 from a database constraint violation. An unhandled constraint error is a server error only in the sense that it was not handled.
  6. Log conflicts with enough context to tell a genuine race from a client retry loop, so a spike can be diagnosed without guesswork.

Still not fixed? Next steps

  • Read the response body. RFC 9110 expects a 409 to describe the conflict well enough for a client to act on it, so the field or resource at fault should be named there.
  • Work out which of the two families you are in: a uniqueness clash on create, or a version clash on update. They look identical from the status code and need completely different fixes.
  • For update conflicts, adopt ETag and If-Match rather than last-write-wins. The 409 is telling you that concurrent edits exist, and silently overwriting them is the failure mode it is protecting you from.
  • For create conflicts, check whether the caller is retrying after a timeout. Idempotency keys turn that from a duplicate or a 409 into a correct repeat of the original response.
  • If 409s spike suddenly on an endpoint that was quiet, look for a client retry loop rather than a data problem. A loop that resends an identical conflicting request will generate them indefinitely.

Code & configuration

Copy-paste starting points. Replace example.com and the paths with your own, and test changes on staging before production.

Use ETag and If-Match to make conflicts detectable

# read, and keep the ETag
curl -i https://api.example.com/docs/42
# ETag: "v7"

# write only if nobody else has changed it since
curl -i -X PUT https://api.example.com/docs/42 \
  -H 'If-Match: "v7"' -d @doc.json
# 409 or 412 if the resource moved on; your write is refused, not silently lost

An idempotency key turns a retry into a repeat, not a conflict

curl -X POST https://api.example.com/charges \
  -H "Idempotency-Key: 7f3a9c12-order-8891" \
  -d '{"amount":2500}'
# a retry with the same key returns the original result
# without it, the server cannot tell a retry from a second charge

Return the conflict, do not leak a constraint violation as a 500

try {
    $repo->create($user);
} catch (UniqueConstraintViolation $e) {
    http_response_code(409);
    echo json_encode(["error" => "email already registered",
                      "field" => "email"]);
}

How to prevent 409 Conflict

A 409 rarely takes a service down, which is exactly why it goes unnoticed: the API answers quickly, every dashboard stays green, and writes fail one user at a time. The dangerous version follows a deployment that changes a uniqueness constraint or concurrency handling, when a previously rare conflict becomes the normal outcome. Pulsetic checks your website and endpoints from multiple locations every 30 seconds and alerts you by email, SMS, voice call, Slack, Discord, Telegram, or webhook the moment a URL stops returning the status code you expect, so a shift like that surfaces while it is still a deploy to roll back.

Learn how Pulsetic's uptime monitoring detects this from the outside, across 15+ locations.

Sources and further reading

The specifications and vendor documentation this guide is written from, plus deeper reading on the parts it only summarises.

Frequently asked questions

  • What does 409 Conflict mean?

    It means the server understood your request and found nothing wrong with it, but could not carry it out because it clashes with the current state of the resource. The two everyday cases are trying to create something whose unique value is already taken, and trying to update a resource that changed after you read it. The request was fine; the state it assumed was not.

  • What is the difference between 409 and 422?

    A 422 means a value in your request broke a validation rule, which would fail no matter what was stored on the server. A 409 means every value was acceptable and the request only failed because of what already exists. The quickest test is to ask whether the same request would succeed against an empty database. If it would, the failure is about state, so 409 is correct.

  • How do I fix a 409 Conflict?

    Re-read the current state of the resource and reconcile your change against it before resending. For a duplicate, either use the resource that already exists or choose a different value. For a concurrent update, fetch the latest version, reapply your change on top of it, and send it back with a fresh ETag. What does not work is resending the identical request, because the conflict is still there.

  • Why do I get a 409 when creating a user that does not exist?

    Usually because a previous attempt did create it and you did not see the response, most often after a timeout on a slow request. The record exists even though your client never received confirmation. Query for the resource before assuming it failed, and adopt idempotency keys so a retry returns the original result rather than colliding with it.

  • Is a 409 a server error?

    No, it sits in the 4xx client-error range, and it is a deliberate response rather than a fault. The server is working correctly and is protecting data it would otherwise lose. The one case where a 409 hints at a server-side problem is when it is really an unhandled database constraint violation that should have been caught and turned into a clear conflict response instead of leaking through.

  • Should I retry a 409 automatically?

    Only with reconciliation in between, never as a blind repeat. The correct loop re-reads the resource, reapplies the change to the current version, and resubmits, which is exactly what Kubernetes controllers do with their retry-on-conflict helpers. Add a retry ceiling too, because a conflict that survives several reconciled attempts is a data problem that more attempts will not solve.

  • What does a 409 have to do with ETags?

    ETags are how a client and server agree on which version of a resource is being edited. You read a resource, keep its ETag, and send it back in an If-Match header when you write. If the resource has moved on, the server refuses the write instead of silently discarding whoever changed it in the meantime. Without that mechanism, concurrent edits tend to be lost quietly rather than reported as a conflict at all.

  • What is the difference between 409 Conflict and 412 Precondition Failed?

    The difference is who checked. A 412 comes back when you attached a precondition yourself, usually an If-Match header carrying an ETag, and the server found it no longer held. A 409 comes back when you attached no precondition at all and the server detected a clash on its own, such as a unique value already being taken. Put simply, 412 means your stated assumption was wrong, and 409 means the server found a conflict you never asked it about.

  • Should creating a duplicate return 409 or 422?

    Return 409 when the value is perfectly valid and the only problem is that it already exists, and 422 when the value itself breaks a rule. Registering ada@example.com when that address is already taken is a 409, because nothing about the address is wrong. Registering not-an-email is a 422, because the value fails validation regardless of what is in the database. The test is whether the same request would succeed against an empty database: if it would, the failure is about state, so 409 is the right code.

  • Can a 409 be safely retried?

    Only after re-reading the current state. Retrying the identical request usually reproduces the same conflict, because the state that caused it has not changed. The correct pattern is to fetch the resource again, merge or reconcile your change against what is now there, and resend with a fresh ETag. Blind retries on a 409 are worse than useless, since they can also mask a genuine data problem behind a loop that never converges.

  • Why does Kubernetes return 409 so often?

    Kubernetes uses optimistic concurrency on every object through resourceVersion. When you submit an update carrying a version that is no longer current, the API server rejects it with a 409 rather than overwriting whatever changed in between. Controllers are written to expect this: they re-read the object, reapply their change, and submit again. A 409 there is normal operation rather than a fault, which is why the client libraries have retry-on-conflict helpers built in.

  • How do idempotency keys relate to 409?

    They prevent one of the most common causes of it. When a client retries a create request after a timeout, without an idempotency key the server has no way to tell a retry from a genuine second attempt, so it either creates a duplicate or returns 409. With a key, the server recognises the repeat and returns the original result instead. Payment APIs lean on this heavily, because a duplicate charge is far more expensive than a duplicate row.

Trusted by teams at companies around the world