DEVESSENTIALS

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.

4xx · Client errorRFC 9110 §15.5.10

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 /users with 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 again is a 409, because the resourceVersion you sent is stale. Creating an object whose name is taken (AlreadyExists) is also 409.
  • Amazon S3: BucketAlreadyExists and BucketAlreadyOwnedByYou are 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 type to branch on, e.g. duplicate vs version conflict.
ETag
If you use ETags for versioning, clients can send If-Match and you can answer 412 instead of 409 for stale writes.

409 vs similar status codes

  • 412 Precondition Failed: For conditional requests: If-Match or If-Unmodified-Since evaluated 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

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

Browse all developer guides →

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.