sfw/fix
428 medium

428 Precondition Required

The server rejects the write because the client sent no conditional header, forcing you to prove you're editing the version you think you are.

What you see

428 Precondition Required

{
  "error": "required precondition is missing",
  "detail": "This request requires an If-Match header."
}

What’s actually happening

A GET works but a PUT, PATCH, or DELETE comes back 428. The API is refusing the write on purpose: it won't let you overwrite a resource blind. It wants you to send the ETag (or last-modified date) you last saw, so it can confirm nobody changed the record between your read and your write. This is the API designer deliberately closing the lost-update hole, where two clients both GET, both edit, and the second save silently clobbers the first.

Common causes

  • Your write request omits the If-Match header (or If-Unmodified-Since) that the endpoint requires.
  • Your HTTP client strips the ETag from the GET response, so you never have a validator to send back.
  • You're hand-testing in curl/Postman and simply forgot to copy the ETag from the read into the write.
  • An API gateway policy enforces conditional requests on a whole route, and a new endpoint slipped under it without the client being updated.
  • You cached the ETag but send it on the wrong header (If-None-Match instead of If-Match), which the server treats as missing.

How to fix it

  1. GET the resource and capture its ETagRead the record first and grab the ETag response header, e.g. ETag: "a1b2c3". This is the validator the server is asking for. Store it alongside the data in your client state.
  2. Send it back on If-Match for the writeOn the PUT/PATCH/DELETE, add If-Match: "a1b2c3". If the API uses dates instead, send If-Unmodified-Since with the Last-Modified value from the GET. The 428 turns into a normal 200/204.
  3. Stop your HTTP layer from discarding the ETagSome wrappers normalize away response headers. Log the raw GET response and confirm the ETag survives to your code. If your framework auto-handles ETags (e.g. some ORMs / API clients), enable that feature instead of doing it by hand.
  4. Read the error body for the exact header nameA spec-compliant 428 names the precondition it wants. If it asks for If-Match and you've been sending If-None-Match, that's why it still reads as missing, they're different operators.

Stop it recurring

Wire ETag capture-and-resend into your shared API client once, so every mutating call carries the validator without each caller remembering.

Related errors