sfw/fix
409 medium

409 Conflict

The request clashes with the resource's current state, such as a concurrent edit, duplicate key, or stale version.

What you see

409 Conflict
The request could not be completed due to a conflict with the current state of the resource.

What’s actually happening

A save, create, or push fails not because the request is malformed but because it collides with where the resource is right now. Two people editing the same record and saving over each other, an account signup with an email that already exists, or an API write against a version that's since moved on all surface as 409. Git throws the same family of conflict when your local branch is behind the remote. The fix is almost always refetch-then-retry, not resend-as-is.

Common causes

  • Optimistic concurrency control: the request carried an ETag / version number that no longer matches the server's current version.
  • A uniqueness constraint hit — creating a record with an email, slug, or ID that already exists.
  • Two concurrent writes to the same resource, where the second arrives after the first changed the state it assumed.
  • A git push rejected because the remote has commits you don't have locally (non-fast-forward).
  • A workflow/state-machine violation — trying to transition a resource into a state it can't move to from its current one (e.g. shipping an already-cancelled order).

How to fix it

  1. Read the response body, not just the code409 bodies usually name the conflict — which field is duplicate, which version mismatched. The status alone is too vague; the body tells you whether to dedupe, refetch, or change the request.
  2. Refetch the latest, reapply, retryFor version conflicts, GET the current resource, rebase your change onto its new state (or merge fields), and resubmit with the fresh ETag/version. Don't just resend the stale payload — it'll 409 again.
  3. Handle duplicates explicitlyOn a uniqueness 409, decide the intent: surface 'email already registered' to the user, or treat it as idempotent and return the existing record. Don't retry the identical insert in a loop.
  4. For git, pull/rebase then pushgit pull --rebase to replay your commits on top of the remote, resolve any merge conflicts in the listed files, then git push. Force-push only when you genuinely own the branch and understand what you're overwriting.
  5. Add idempotency keys to writesFor create endpoints, send a client-generated idempotency key so a retried request returns the original result instead of conflicting. Stripe-style APIs do this to make retries safe.

Stop it recurring

Use ETags/version numbers with refetch-and-retry, and add idempotency keys to create endpoints so retries don't collide.

Related errors