409 Conflict: When a Request Clashes With Current State

Status 409 Conflict means your request was understood but collides with the current state of the thing it targets. Typical cases are creating a user or file that already exists, saving over a newer version someone else wrote, or pushing a stale revision. Refresh the state, resolve the clash, then resend.

Where 409 shows up in practice
Service or caseWhat clashedUsual fix
Sign-up or create APIEmail or username already takenShow a clear message or switch to update
Optimistic lockingVersion number older than the stored oneReload, merge changes, retry
Kubernetes APIresourceVersion changed since you read itGet the object again and reapply
Cloud storage bucketsBucket name already in use or not emptyPick another name or empty it first
CouchDB documentsMissing or old _revFetch the latest _rev and resend

Updated

Unlike 400, the request is well formed. It would have worked a moment earlier or with different data. That makes 409 a signal to look at state on the server, not at syntax, and usually means the client needs to read before it writes again.

Send and handle 409

Map a unique constraint to 409 in Express

23505 is Postgres unique_violation; other databases use their own codes.

try {
  await db.query("INSERT INTO users(email) VALUES ($1)", [email]);
  res.status(201).end();
} catch (err) {
  if (err.code === "23505") {
    return res.status(409).json({ error: "Email already registered", field: "email" });
  }
  throw err;
}

React to 409 on the client

Reload the latest copy instead of retrying the same body.

const res = await fetch(url, { method: "PUT", body, headers });
if (res.status === 409) {
  const latest = await fetch(url).then((r) => r.json());
  showConflict(latest);
}

Handling 409 in a client

Treat 409 as a branch in your code, not a crash. For duplicates, show the user what already exists. For edit conflicts, load the server copy and either merge automatically or show both versions so a person can pick.

Collaborative editors and form tools avoid lost updates exactly this way: the save carries the version you started from, and the server refuses it when someone else saved in between.

Returning 409 from your own API

Catch the database's unique constraint error, such as Postgres code 23505, and map it to 409 with a message naming the field. Letting it bubble up turns a predictable clash into a 500.

If all you need is a business site rather than an API with conflict rules, We.Inc can build and publish it without any of this server code.

How it works

  1. Read the response body, which normally names the field or version that clashed.
  2. Fetch the current version of the resource.
  3. Decide whether to merge, overwrite deliberately, or ask the user.
  4. Resend the request with the new version or changed data.

Tips

  • Retrying a 409 unchanged in a loop never helps. Unlike 429 or 503, the conflict will still be there.
  • If you design an API, send 409 for duplicates and version clashes and include which field conflicted, so clients can show a useful message.
  • With ETags, send If-Match on updates; servers commonly answer a stale one with 412 Precondition Failed rather than 409, so handle both.
  • Double-clicked submit buttons often create a duplicate on the second request. Disable the button while the first request runs.

Frequently asked questions

What does a 409 response code mean?

It means Conflict: the server cannot complete the request because it conflicts with the target resource's current state. The client is expected to resolve the conflict and try again.

Is 409 a client or server error?

It is in the 4xx range, so it is a client error. The server is working; the request needs different data or a fresher version.

Should a duplicate email return 409 or 400?

409 is the common choice because the input is valid but clashes with existing data. Some teams use 422. Pick one and document it for API users.

How do I fix a 409 Conflict in Kubernetes?

The object changed after you read it. Get it again with kubectl get, apply your change to that fresh copy, and submit, or use kubectl apply, which handles merging for you.

Related: HTTP status codes, 400 Bad Request, 429 Too Many Requests, GraphQL mutations.

Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.

More in Guides

Product

Who It's For

Features

Resources

Company

View Sitemap