HTTP 409 Conflict
HTTP 409 Conflict means the request cannot be completed because it conflicts with the current state of the target resource. The resource exists and the request is well-formed — but fulfilling it would create an inconsistency that the server will not allow. 409 is retryable once the conflict is resolved, and the response body should explain exactly what the conflict is and how to resolve it.
Quick reference
| Code | 409 |
|---|---|
| Name | Conflict |
| Category | 4xx Client Error |
| Specification | RFC 9110 §15.5.10 |
| Retryable? | Yes — after resolving the conflict |
| Cacheable? | No |
Common causes
Duplicate unique constraint
A POST to create a resource with a value that must be unique (email address, username, slug, SKU) when that value already exists. The resource cannot be created because it would violate uniqueness. Return 409 with a body that identifies the conflicting field.
POST /api/users HTTP/1.1
Content-Type: application/json
{"email": "alice@example.com", "name": "Alice"}
HTTP/1.1 409 Conflict
{"error": "conflict",
"field": "email",
"message": "An account with this email already exists."}
Version conflict (optimistic locking)
A PUT to update a document that was modified by another client since the current client fetched it. The client’s version of the resource is stale. Return 409 (or 412 if using conditional request headers).
PUT /api/documents/42 HTTP/1.1
Content-Type: application/json
{"version": 3, "title": "New title"}
HTTP/1.1 409 Conflict
{"error": "version_conflict",
"current_version": 5,
"message": "Document was modified; fetch the latest version and retry."}
State machine violation
A transition that is not allowed in the current resource state. For example, attempting to cancel an order that has already shipped, or trying to publish a document that is locked for review.
POST /api/orders/7829/cancel HTTP/1.1
HTTP/1.1 409 Conflict
{"error": "invalid_state_transition",
"current_state": "shipped",
"message": "Cannot cancel an order that has already shipped."}
Circular dependency
In hierarchical data structures, adding a child that would create a cycle. For example, making Category A a child of Category B when B is already a child of A creates a circular reference that the server refuses.
Response body requirements
RFC 9110 states that for 409, the server “SHOULD generate a representation containing enough information for a user to recognize the source of the conflict.” A 409 without a descriptive body is only marginally better than a 500 — the client knows there is a conflict but cannot act on it.
A useful 409 body includes: the error type (machine-readable string), the conflicting field or entity, and a human-readable message explaining what needs to change. For version conflicts, include the current server-side version or ETag. For state conflicts, include the current state and which transitions are allowed.
{
"error": "duplicate_field",
"field": "email",
"value": "alice@example.com",
"message": "An account with this email already exists.",
"link": "https://api.example.com/docs/errors/duplicate_field"
}
Server-side implementation
Express.js with PostgreSQL unique constraint handling:
app.post('/api/users', async (req, res) => {
try {
const user = await db.createUser(req.body);
res.status(201).json(user);
} catch (err) {
if (err.code === '23505') { // PostgreSQL unique violation
return res.status(409).json({
error: 'duplicate_field',
message: 'A user with this email already exists.'
});
}
res.status(500).json({error: 'internal_error'});
}
});
Django REST Framework:
from django.db import IntegrityError
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
def create(self, validated_data):
try:
return super().create(validated_data)
except IntegrityError:
raise serializers.ValidationError(
{"email": "An account with this email already exists."}
)
409 vs 412 vs 422 vs 423
| Code | Meaning | Use case |
|---|---|---|
| 409 | Conflict | Resource state prevents this operation (duplicate, version, state machine) |
| 412 | Precondition Failed | Conditional If-Match header failed; used with optimistic locking |
| 422 | Unprocessable Content | Request syntactically valid but semantically invalid (field validation errors) |
| 423 | Locked | Resource is locked by another session (WebDAV) |
A useful heuristic: if the conflict involves the relationship between submitted data and existing system data (duplicate email, version mismatch, circular reference), use 409. If the conflict is within the submitted data itself (missing field, invalid format), use 422.
Frequently asked questions
What does HTTP 409 mean?
The request conflicts with the current state of the resource. Most common cases: creating a resource that already exists (duplicate unique field), updating a stale version of a resource (optimistic locking conflict), or attempting a state transition that is not allowed from the resource’s current state.
Is HTTP 409 retryable?
Yes, but not by simply resending the same request. The client must resolve the conflict first: for duplicates, check if the existing resource is what was intended; for version conflicts, re-fetch the latest version and apply changes to it; for state conflicts, check what transitions are allowed from the current state.
When should I use 409 vs 422?
Use 409 when the conflict involves the relationship between submitted data and existing system state. Use 422 when the request data fails validation independent of system state.
Can 409 be cached?
No. 409 is not cacheable. The conflict depends on the current system state, which changes over time. A request that conflicts today may succeed after the conflict is resolved.
Related guides
HTTP 412 Precondition Failed · HTTP 428 Precondition Required · HTTP 422 Unprocessable Content
Comparisons
Standards reference
Definitions from the IANA HTTP Status Code Registry and RFC 9110 §15.5.10. Human-readable guidance by ErrorLookup. · HTTP 409 quick reference →