Skip to main content
409

Conflict

Active
RFC 9110 §15.5.10Since 1997REST APIs, databases, version control, state machines

HTTP 409 Conflict indicates the request could not be completed due to a conflict with the current state of the target resource. Defined in RFC 9110 §15.5.10. The user might be able to resolve the conflict and resubmit. Common in version control, unique constraints, and optimistic concurrency.

Description

The 409 Conflict status code indicates that the request could not be completed due to a conflict with the current state of the target resource. This code is used in situations where the user might be able to resolve the conflict and resubmit the request.

The server SHOULD generate content that includes enough information for a user to recognize the source of the conflict. Conflicts are most likely to occur in response to a PUT request — for example, if versioning were being used and the representation being PUT included changes to a resource that conflict with those made by an earlier request.

409 is fundamentally different from 400 or 422. Those mean the request itself is wrong. 409 means the request is perfectly valid but the world has changed underneath it — another request got there first, or the resource is in a state that makes this operation impossible right now.

The defining characteristic: the same request might succeed if sent again after the conflict is resolved. The client isn't broken — the timing or state is.

Examples

Creating a resource with duplicate unique field
http
POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer token123

{"email": "[email protected]", "name": "Alice Smith"}
409 response — unique constraint violation
http
HTTP/1.1 409 Conflict
Content-Type: application/json

{"error": "conflict", "message": "A user with email [email protected] already exists.", "conflicting_field": "email", "existing_resource": "/api/users/42"}
Optimistic concurrency — stale ETag
http
PUT /api/documents/17 HTTP/1.1
Host: api.example.com
Content-Type: application/json
If-Match: "v3-abc123"

{"title": "Updated Title", "body": "New content..."}
409 response — version conflict
http
HTTP/1.1 409 Conflict
Content-Type: application/json
ETag: "v4-def456"

{"error": "conflict", "message": "Resource was modified by another request. Your version: v3, current version: v4.", "current_etag": "v4-def456", "your_etag": "v3-abc123", "modified_by": "user/89", "modified_at": "2024-01-15T10:28:00Z"}
State machine violation
http
POST /api/orders/55/ship HTTP/1.1
Host: api.example.com
Authorization: Bearer admin-token
409 — invalid state transition
http
HTTP/1.1 409 Conflict
Content-Type: application/json

{"error": "invalid_state_transition", "message": "Cannot ship order 55: current state is 'cancelled'. Only orders in state 'paid' can be shipped.", "current_state": "cancelled", "allowed_transitions": ["refund", "archive"]}

Edge Cases

  • 409 implies the conflict is RESOLVABLE — always include enough information for the client to fix it (conflicting field, current version, allowed states).
  • For optimistic concurrency: return the current ETag in the 409 response so the client can fetch the latest version, merge, and retry.
  • 409 is NOT for validation errors — 'invalid email format' is 422, but 'email already taken' is 409 (it's about resource state, not request validity).
  • Some APIs return 409 for rate limiting or resource locking — technically acceptable if framed as 'resource is in a conflicting state (locked by another process)'.
  • Git push rejection is conceptually a 409 — your branch diverged from the remote. The resolution is to pull, merge, and push again.
  • Database deadlocks that bubble up to the HTTP layer often manifest as 409 — the client can retry after a brief delay.
  • For bulk operations: if one item in a batch conflicts, decide whether to fail the entire batch (409 on the batch endpoint) or return partial success with per-item status.

When You'll See This

  • Duplicate email or username on user registration
  • Optimistic lock failure — another user edited the document between your read and write
  • Git push rejected because remote has diverged
  • Trying to delete a resource that has dependent children (foreign key constraint)
  • State machine violation: publishing an already-archived article
  • Creating a resource with a slug/ID that already exists
  • Concurrent requests trying to claim the same limited resource (ticket, reservation slot)

Implementation References

LanguageConstant
Gohttp.StatusConflict
Rusthttp::StatusCode::CONFLICT
Pythonhttp.HTTPStatus.CONFLICT
Node.jshttp.STATUS_CODES[409]
.NETHttpStatusCode.Conflict
JavaHttpURLConnection.HTTP_CONFLICT
PHPResponse::HTTP_CONFLICT (Symfony)
Ruby:conflict (Rack)

History

Introduced in HTTP/1.1 (RFC 2068, 1997). Originally envisioned for PUT conflicts where two users edit the same resource simultaneously. Became critical with the rise of RESTful APIs that enforce unique constraints, versioning (ETags), and state machines. The optimistic concurrency pattern (If-Match + ETag + 409 on mismatch) is now a standard pattern in distributed systems.

Related Status Codes

Related Headers

FAQ

When should I use 409 Conflict vs 400 Bad Request?

Use 400 when the request itself is malformed (bad JSON, missing fields). Use 409 when the request is perfectly valid but conflicts with existing state — duplicate unique field, stale version, or invalid state transition. The distinction: 400 means 'your request is wrong', 409 means 'your request is fine but the world disagrees right now'.

How does 409 work with optimistic concurrency and ETags?

The client GETs a resource and receives an ETag (version identifier). When updating, it sends If-Match with that ETag. If another client modified the resource in between (new ETag), the server returns 409 with the current ETag. The client fetches the latest version, merges changes, and retries with the new ETag.

What information should a 409 response body contain?

Include: (1) what conflicted (field name, resource ID), (2) the current state (current ETag, existing resource URL, current lifecycle state), (3) how to resolve it (merge and retry, use different value, transition through allowed states). The goal is making the conflict resolvable without additional lookups.