Skip to content
HTTP 409 Conflict — What It Means & How to Debug

HTTP 409 Conflict — What It Means & How to Debug

DodaTech Updated Jun 20, 2026 4 min read

HTTP 409 Conflict is an HTTP response status code that indicates the request could not be completed because it would create a conflict with the current state of the target resource. This typically occurs in systems that enforce data integrity constraints or implement optimistic concurrency control.

What It Means

Defined in RFC 7231 Section 6.5.8, the 409 status code is the server’s way of telling the client that its representation of the resource is stale or that its intended action violates a domain constraint. The response body SHOULD contain enough information for the client to understand the nature of the conflict and how to resolve it.

Unlike a 412 Precondition Failed, which checks conditional headers like If-Match, a 409 is about semantic state conflicts at the application level.

When It’s Sent

  • Duplicate resource creation — Attempting to create a user with an email that already exists.
  • Optimistic locking failure — Two clients read a document, both modify it, and the second one tries to save using a stale version identifier.
  • State transition violation — Trying to cancel an already-shipped order; the workflow engine rejects the transition.
  • Concurrent edits on a wiki — A collaborative editing platform detects that the underlying document has changed since the user started editing.
  • Directory or namespace conflicts — Creating a file or directory that collides with an existing one in a version-controlled store.

Real Example

The following curl command attempts to create a resource that already exists, triggering a 409:

curl -v -X POST https://jsonplaceholder.typicode.com/posts \
  -H "Content-Type: application/json" \
  -d '{"id": 1, "title": "foo", "body": "bar", "userId": 1}'

Expected response:

< HTTP/2 201
...

Free test APIs often return 201 without enforcing uniqueness. A real-world API enforcing uniqueness would respond with:

< HTTP/1.1 409 Conflict
< Content-Type: application/json
<
{
  "error": "Conflict",
  "message": "A post with id '1' already exists.",
  "existing_resource": "/posts/1"
}

For an optimistic locking scenario, the request might use an If-Match header with an ETag:

curl -v -X PUT https://api.example.com/documents/42 \
  -H "If-Match: \"abc123\"" \
  -H "Content-Type: application/json" \
  -d '{"title": "Updated"}'

If the document has been modified since abc123 was generated:

< HTTP/1.1 409 Conflict
< Content-Type: application/json
<
{
  "error": "Conflict",
  "current_etag": "\"def456\"",
  "message": "Document was modified by another user. Re-fetch and re-apply your changes."
}

How to Debug

Client-Side

  1. Re-fetch the resource — GET the current state of the resource and compare it with your local copy. Re-apply your changes on top of the fresh version.
  2. Check for duplicate keys — If you are creating a resource, verify that the unique identifiers (email, username, slug) are not already in use.
  3. Inspect the response body — The server should describe the conflict in detail. Look for fields like current_value, expected_value, or conflicting_field.
  4. Implement retry with refresh — For concurrent edit scenarios, fetch the latest version, merge changes, and retry the update with the new version identifier.

Server-Side

  1. Log conflict details — Include the conflicting fields, expected vs actual state, and the user/actor making the request.
  2. Use optimistic concurrency — Require If-Match / If-Unmodified-Since headers on write operations and return 409 when they mismatch.
  3. Return actionable error bodies — Include a conflicts array with field-level details so the client knows exactly what to fix.
  4. Design idempotent APIs — Use upsert semantics (PUT creates or updates) to reduce the surface area for conflicts.

Common Causes Table

ScenarioLikely CauseHow to Fix
Creating duplicate userEmail or username already registeredCheck uniqueness before POST; return helpful error
Save conflict in collaborative appTwo users edit the same document simultaneouslyImplement OT/CRDT or optimistic locking with ETags
Order state transition rejectedWorkflow engine disallows the changeValidate state transitions server-side; suggest allowed transitions
Upsert PUT returns 409Missing idempotency key or preconditionInclude idempotency key; use conditional headers
Directory creation failsParent path contains a file with the same nameCheck namespace before creating; return descriptive error

FAQ

How is 409 different from 412 Precondition Failed?
A 412 is returned when conditional request headers (e.g., If-Match, If-Unmodified-Since) evaluate to false. A 409 is about application-level semantic conflicts — the resource state is incompatible with the request regardless of headers.
Can a 409 error be resolved automatically?
In some cases, yes. If you optimistically lock a resource, you can re-fetch it, merge your changes programmatically, and retry. For duplicate creation errors, an upsert (PUT instead of POST) may succeed if the API supports it.
Should I expose conflict details to end users?
Expose readable messages but avoid leaking internal identifiers or stack traces. For example, “A user with this email already exists” is fine; including the database row ID is not.

Related Codes

HTTP 412 Precondition Failed — A precondition header evaluated to false.

HTTP 428 Precondition Required — The server requires conditional headers on write requests.

HTTP 404 Not Found — The requested resource was not found.

HTTP 410 Gone — The requested resource is permanently gone.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro