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.

HTTP 409 quick reference →

Quick reference

Code409
NameConflict
Category4xx Client Error
SpecificationRFC 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

CodeMeaningUse case
409ConflictResource state prevents this operation (duplicate, version, state machine)
412Precondition FailedConditional If-Match header failed; used with optimistic locking
422Unprocessable ContentRequest syntactically valid but semantically invalid (field validation errors)
423LockedResource 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

HTTP 409 vs 422

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 →