The HTTP 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 guide explains common 409 scenarios, REST API patterns for handling conflicts, race condition handling, and the differences between 409, 422 Unprocessable Entity…
HTTP 409 Conflict is the response code a server returns when the request could not be completed due to a conflict with the current state of the target resource. Per RFC 9110, conflicts are most likely to occur in response to PUT requests where the request would result in conflicting versions of the resource. The conflict response should include enough information for the user to recognize the source of the conflict and ideally resubmit a corrected request. The status code is part of the 4xx client error family — the server is saying 'your request is well-formed but cannot be processed because of how it interacts with current resource state.'
(1) Duplicate resource creation — POST /users with a username that already exists. (2) Version conflicts in concurrent editing — two users edit the same document; the second save's version number doesn't match the current server version. (3) State machine violations — attempting to ship an order that's already shipped, or cancel a payment that's already settled. (4) File system conflicts — uploading a file with a path that's already taken when the API doesn't auto-rename. (5) Inventory conflicts — purchasing the last item when another transaction already claimed it. (6) Account locking — attempting an action that conflicts with a security hold or pending review on the resource.
Both are 4xx client errors but they describe different problems. 409 indicates a state conflict — the request itself is valid, but the resource is in a state that doesn't permit the action (or the action would create inconsistent state). 422 indicates that the request payload was understood but is semantically invalid — for example, sending a date in the wrong format, providing a negative number where positive was required, or violating a business rule that's intrinsic to the data rather than to the resource state. Rule of thumb: if changing the request would fix it, use 422; if changing the resource's state would fix it, use 409.
412 Precondition Failed is a more specific case of conflict-detection. 412 is returned specifically when the client sent precondition headers (If-Match, If-None-Match, If-Unmodified-Since) and those preconditions weren't satisfied — typically because the resource was modified since the client last fetched it. 409 is more general — it can apply when no preconditions were sent but the request still conflicts with current state. Best practice: implement optimistic concurrency control by having clients send If-Match headers with ETags; return 412 when those preconditions fail. Use 409 for conflicts that aren't expressible as HTTP preconditions (state machine violations, duplicate creation, business-rule conflicts).
Race conditions occur when multiple requests interact with the same resource concurrently and produce inconsistent results. The classic 409 use case: two users attempt to claim the last available inventory item simultaneously. The proper handling pattern: (1) Wrap inventory check and decrement in a database transaction with proper isolation level (typically SERIALIZABLE or use SELECT ... FOR UPDATE). (2) When the second request finds inventory already claimed, return 409 with a clear message. (3) Include suggested resolution in the response body — for example, 'this item is no longer available; here are similar items.' (4) Make the operation idempotent where possible to allow safe retries. Without proper transactional handling, race conditions can corrupt data; without proper 409 responses, clients can't distinguish race-condition failures from other errors. Our team's perspective on HTTP 409 conflict comes from active client work, not theory.
Optimistic locking is the standard pattern for preventing concurrent edit conflicts. The pattern: (1) When a client fetches a resource, the server includes a version identifier (ETag header, version number in the body). (2) When the client sends an update, it includes the version it last saw (typically via If-Match header). (3) If the server's current version matches, apply the update and return the new version. (4) If versions don't match, return 409 (or 412 if using preconditions) with information about the current state. The client can then refetch, merge changes, and retry. This pattern scales well, requires no server-side locking, and gives users meaningful conflict resolution opportunities. It's the standard approach in REST APIs from Google, Microsoft, GitHub, and most major SaaS platforms. Considering HTTP 409 conflict? Book a no-pressure strategy call to compare options.
A well-formed 409 response should include: (1) The 409 status code itself. (2) A response body explaining the specific conflict (which field conflicts, what state prevents the action). (3) Where applicable, the current state of the resource so the client can decide how to proceed. (4) Suggested resolution — for example, 'use PATCH /users/123/email with the new email and the current ETag.' (5) A unique error code for programmatic handling (different from the HTTP status). For APIs following common conventions, the response body might look like: { 'error': 'conflict', 'message': 'Username already exists', 'conflicting_field': 'username', 'suggestion': 'Try a different username or sign in to existing account' }.
(1) Using 400 Bad Request for state conflicts — 400 should be reserved for malformed requests; use 409 for state conflicts. (2) Using 500 Internal Server Error for race conditions — race conditions are not server errors; they're expected client interactions that need 409. (3) Returning 409 with no explanation in the body — leaves the client unable to resolve the conflict. (4) Confusing 409 with 422 — 409 is about state conflicts, 422 about request semantics. (5) Failing to handle 409 in client retry logic — clients should treat 409 as a non-retryable error (without modification) rather than blindly retrying. (6) Missing transactional safety — returning 409 without proper database transactions can still allow data corruption. Our recent HTTP 409 conflict engagements informed every recommendation on this page.