HTTP 409 Conflict
409 says: the request makes sense in general, but not against the resource as it is right now. The client may be able to resolve the conflict and resubmit, which is why RFC 9110 asks the server to include enough information for the user to recognise the source of the conflict.
409 Conflict
The request conflicts with the current state of the target resource.
Who fixes it: Client. The client has to resolve the conflict (reload, merge, pick another value) before resubmitting. The server's job is to explain the conflict clearly.
What 409 means
The RFC's own example is versioning: a PUT based on an outdated version of the resource. Other common uses are duplicate unique values and invalid state transitions.
If the client used a conditional header such as If-Match and the condition failed, the precise code is 412 Precondition Failed.
Source: RFC 9110 §15.5.10.
Common causes
- Creating a duplicate:
POST /userswith an email or username that's already taken, i.e. a database unique-constraint violation. - Lost update: two people edited the same record, and the second save was based on a stale version.
- Invalid state transition: cancelling an order that has already shipped, or publishing a post that's already published.
- Kubernetes:
the object has been modified; please apply your changes to the latest version and try againis a 409, because theresourceVersionyou sent is stale. Creating an object whose name is taken (AlreadyExists) is also 409. - Amazon S3:
BucketAlreadyExistsandBucketAlreadyOwnedByYouare 409 Conflict errors.
How to return and resolve a 409
Express + PostgreSQL: map unique violations to 409
PostgreSQL reports unique-constraint violations with SQLSTATE 23505. Turn that into a 409 with a problem-details body, not a 500.
app.post('/api/users', async (req, res, next) => {
try {
const user = await db.users.insert(req.body);
res.status(201).location(`/api/users/${user.id}`).json(user);
} catch (err) {
if (err.code === '23505') { // unique_violation
return res.status(409).type('application/problem+json').json({
type: 'https://api.example.com/problems/duplicate-email',
title: 'Email already registered',
status: 409,
});
}
next(err);
}
});Optimistic concurrency with a version number
Update only if the version still matches. If zero rows changed, someone else got there first: return 409 with the current version.
-- SQL
UPDATE docs SET title = $1, version = version + 1
WHERE id = $2 AND version = $3; -- 0 rows updated -> 409
-- HTTP
PATCH /api/docs/42
{ "title": "New title", "version": 7 }
HTTP/1.1 409 Conflict
Content-Type: application/problem+json
{ "title": "Version conflict", "status": 409,
"detail": "Document is at version 8; reload and reapply your change." }Client: resolve, don't blindly retry
The same request will conflict again. Fetch the latest state, merge or ask the user, then resubmit. Kubernetes controllers written with client-go can wrap updates in retry.RetryOnConflict, which re-reads and retries.
Headers that matter with 409
- Content-Type: application/problem+json
- RFC 9457 problem details give clients a stable
typeto branch on, e.g. duplicate vs version conflict. - ETag
- If you use ETags for versioning, clients can send
If-Matchand you can answer 412 instead of 409 for stale writes.
409 vs similar status codes
- 412 Precondition Failed: For conditional requests:
If-MatchorIf-Unmodified-Sinceevaluated to false. - 422 Unprocessable Content: The data is invalid on its own, regardless of what's stored on the server.
- 400 Bad Request: The request is malformed. A 409 request is well-formed but clashes with current state.
Related status codes
422Unprocessable Content
The server understands the content type and syntax of the request, but can't process the instructions it contains.
400Bad Request
The server can't or won't process the request because of something it considers a client error.
201Created
The request succeeded and created one or more new resources.
Looking for another code? Search the full HTTP status codes list, or read which code your API should return in the REST API status codes guide.
Learn more
HTTP Status Codes for REST APIs: 200 vs 201 vs 204, 400 vs 422 vs 429
A practical guide to choosing the right HTTP status code in REST API design. Covers the most confusing pairs: 200 vs 201, 400 vs 422, 401 vs 403, and when to use 503 vs 429.
REST API Design Best Practices: A Practical Guide
The conventions that make REST APIs intuitive and consistent. Covers URL design, HTTP methods, status codes, versioning, pagination, and error responses.
CORS Explained: Why It Happens and How to Fix It
CORS errors happen when a browser blocks a request to a different origin. Learn how the Same-Origin Policy works, what preflight requests are, and how to configure CORS correctly in Express, FastAPI, and Nginx.
Frequently Asked Questions
Should a duplicate email return 409 or 422?▾
409 is the most common choice, because the request is valid but conflicts with existing data. Some APIs use 422 and treat uniqueness as a validation rule. Either is defensible if consistent. On public signup or password-reset forms, any distinct response reveals which emails are registered.
Is it safe to retry a 409?▾
Not unchanged, because the same request will conflict again. Read the current state of the resource, resolve the conflict by merging, choosing a new value or asking the user, then send a new request.
What is the difference between 409 and 412?▾
412 Precondition Failed is for conditional requests: the client sent If-Match or If-Unmodified-Since and the condition was false. 409 is for conflicts the server detects from the request body or the resource's state, such as a version number in the JSON or a duplicate key.