# `conflict`: Idempotency key conflict

> HTTP 409. The `Idempotency-Key` on this write was already used for a different request, or a request with the same key has not finished.

Every write carries an `Idempotency-Key`. Commune records the key with a fingerprint of the request before making the change, and for the next 24 hours a request with the same key from the same credential is treated as a retry of the first: if it is identical, you get the first answer back (with `Idempotent-Replay: true`) and nothing happens twice.

This error is that mechanism refusing something that is not a clean retry. **Nothing was changed by a request that answers `409`.** `param` is `Idempotency-Key`, and the message says which of the two cases you hit.

A write that failed with a `4xx` releases its key, so after fixing a `400` or `422` you may send the corrected request with the same key. A `5xx` keeps the key claimed, because its outcome is unknown.

## Why it happens, and how to fix it

### 1. The key was already used for a different request

The same key arrived with a request that differs from the first one that used it: a different body (even a reordered or reformatted JSON body counts, because the raw bytes are compared), different query parameters, a different path, a different `Commune-Version`, or a different operation altogether. Usually this is code that reuses a constant key, or derives the key from something that does not change between distinct writes.

**Fix:** Generate a fresh key (a UUID) for each distinct change, and reuse a key only to retry the exact same request, byte for byte, including the `Commune-Version` header. Store the key with the pending change so a retry sends it unchanged.

### 2. A request with the same key is still in flight

An earlier request with this key is still running, typically because a client timed out and retried immediately, or two workers picked up the same job. The same happens for up to 60 seconds after an attempt that ended in a `5xx` or never reported an outcome.

**Fix:** Wait the number of seconds in `Retry-After`, then retry with the same key and the same request. You will get the first attempt's answer, marked `Idempotent-Replay: true`, or the claim will have expired and your retry runs.

## Retrying

Only for the in-flight case: wait `Retry-After` and resend the identical request with the same key; for a reused key, send the new request with a new key.

## Headers

- `Retry-After`: Seconds to wait before retrying, sent only when the other request is still in flight (5 seconds).
- `Idempotent-Replay`: Not on this error. It is `true` on a later successful response that was replayed from the first request with the same key rather than performed again.

## Example response

```json
{
  "error": {
    "code": "conflict",
    "message": "The Idempotency-Key \"4d6f8e2a-9b1c-4c3d-8e7f-0a1b2c3d4e5f\" was already used for a different request, on this same operation with different parameters. A key names one change: reuse it only to retry that exact request, and send a new value for a different one. Nothing was changed by this request.",
    "param": "Idempotency-Key",
    "request_id": "req_01j9c8h1q7m3n4p5r6s7t8u9v0",
    "docs_url": "https://usecommune.dev/errors/conflict"
  }
}
```

Often confused with: [`bad_request`](https://usecommune.dev/errors/bad_request.md), [`rate_limited`](https://usecommune.dev/errors/rate_limited.md).
