412 Precondition Failed
4xx client error
A condition in the request headers, such as If-Match, didn't hold, so nothing was changed.
412 is how conditional writes fail safely. A client sends If-Match: "<etag>" or If-Unmodified-Since to say 'only apply this if the resource is still the version I saw'. If someone else changed it in the meantime, the server refuses with 412 instead of silently overwriting their work.
| Class | 4xx Client Error |
|---|---|
| Retry? | No — change the request first |
| Cacheable by default | No — only with explicit Cache-Control |
| Response body | Allowed |
| Defined in | RFC 9110 §15.5.13 — 412 Precondition Failed |
Example response
HTTP/1.1 412 Precondition Failed
Content-Type: application/json
Content-Length: 31
{"error":"Precondition Failed"}Common causes
- Optimistic concurrency: the ETag in
If-Matchis stale. - Cloud storage uploads with a version or generation precondition that no longer matches.
If-None-Match: *on a create, when the resource already exists.
How to fix it
- Re-fetch the resource to get the current ETag, re-apply the change and retry.
- Show users a 'someone else edited this' prompt instead of retrying blindly.
Send a 412 from your server
Express
res.status(412).json({ error: "Precondition Failed" });Next.js route handler
return Response.json({ error: "Precondition Failed" }, { status: 412 });Flask
return {"error": "Precondition Failed"}, 412Go net/http
http.Error(w, "Precondition Failed", http.StatusPreconditionFailed)
nginx
return 412;
Check what a URL returns
Print just the status line from the command line, or check a whole list of URLs at once, with their response headers, in the HTTP Status Checker.
curl -sI https://example.com/your-path | head -n 1
Frequently asked questions
412 vs 409?
412: a precondition the client sent (If-Match and friends) failed. 409: the server found a conflict without one.
412 vs 428?
412: you sent a condition and it failed. 428: you didn't send one and the server requires it.
Headers used with 412
Related status codes