HTTP 409 Conflict — What It Means & How to Debug
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
- 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.
- Check for duplicate keys — If you are creating a resource, verify that the unique identifiers (email, username, slug) are not already in use.
- Inspect the response body — The server should describe the conflict in detail. Look for fields like
current_value,expected_value, orconflicting_field. - 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
- Log conflict details — Include the conflicting fields, expected vs actual state, and the user/actor making the request.
- Use optimistic concurrency — Require
If-Match/If-Unmodified-Sinceheaders on write operations and return 409 when they mismatch. - Return actionable error bodies — Include a
conflictsarray with field-level details so the client knows exactly what to fix. - Design idempotent APIs — Use upsert semantics (PUT creates or updates) to reduce the surface area for conflicts.
Common Causes Table
| Scenario | Likely Cause | How to Fix |
|---|---|---|
| Creating duplicate user | Email or username already registered | Check uniqueness before POST; return helpful error |
| Save conflict in collaborative app | Two users edit the same document simultaneously | Implement OT/CRDT or optimistic locking with ETags |
| Order state transition rejected | Workflow engine disallows the change | Validate state transitions server-side; suggest allowed transitions |
| Upsert PUT returns 409 | Missing idempotency key or precondition | Include idempotency key; use conditional headers |
| Directory creation fails | Parent path contains a file with the same name | Check namespace before creating; return descriptive error |
FAQ
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