Skip to main content
4xx Client Error

412 Precondition Failed

The client indicated preconditions in its headers that the server could not meet.

Meaning & Description

The 412 Precondition Failed status code indicates that one or more conditions given in the request header fields evaluated to false when tested on the server. This is primarily used for conditional requests (e.g., using `If-Match` or `If-Unmodified-Since`) to prevent lost updates in concurrent environments. When a client modifies a resource, it can include the resource's current ETag in an `If-Match` header. If the server's ETag does not match, the server returns 412, meaning another client has modified the resource in the meantime.

Common Causes

  • Two users attempting to update the same document simultaneously (a race condition).
  • A client sending an outdated ETag in the `If-Match` header.
  • A client attempting to modify a file that has been changed since the timestamp provided in `If-Unmodified-Since`.

How to fix a 412 error

  1. Perform a GET request to retrieve the latest version of the resource and its current ETag.
  2. Update the `If-Match` header in your subsequent PUT/PATCH request with the new ETag.
  3. If using timestamp-based conditions, ensure clock synchronization between the client and server.
  4. Prompt the end-user to resolve the conflict (e.g., "The document was modified by another user. Review their changes before saving").

Browser & SEO Behaviour

Browser Behavior

Browsers typically do not issue `If-Match` headers on their own for navigation, but XHR/Fetch API calls using conditional headers will receive this and bubble the error to the JavaScript application.

SEO Impact

No direct SEO impact. Search engine crawlers do not typically send `If-Match` or `If-Unmodified-Since` headers that would trigger a 412.

CDN Behavior

CDNs will usually proxy conditional requests to the origin. If the origin returns 412, the CDN passes it back to the client and generally does not cache it.

Code Examples

HTTP
HTTP/1.1 412 Precondition Failed
Content-Type: application/json

{
  "error": "ETag mismatch. The resource has been modified."
}
Node.js (Express)
app.put("/docs/:id", async (req, res) => {
  const doc = await getDoc(req.params.id);
  const clientETag = req.headers["if-match"];

  if (clientETag && clientETag !== doc.etag) {
    return res.status(412).json({ error: "Precondition Failed" });
  }
  
  // Proceed with update
  await updateDoc(req.params.id, req.body);
  res.send("Updated successfully");
});
Python (FastAPI)
from fastapi import FastAPI, Header, HTTPException

app = FastAPI()

@app.put("/items/{item_id}")
async def update_item(item_id: int, if_match: str = Header(None)):
    current_etag = `"123456"`
    if if_match != current_etag:
        raise HTTPException(status_code=412, detail="Precondition Failed")
    return {"message": "Item updated"}
Go (net/http)
func handler(w http.ResponseWriter, r *http.Request) {
    currentETag := "\"123456\""
    ifMatch := r.Header.Get("If-Match")

    if ifMatch != "" && ifMatch != currentETag {
        http.Error(w, "Precondition Failed", http.StatusPreconditionFailed)
        return
    }
    w.WriteHeader(http.StatusOK)
}
C# (ASP.NET Core)
[HttpPut("{id}")]
public IActionResult Update(int id, [FromHeader(Name = "If-Match")] string ifMatch)
{
    var currentETag = "\"123456\"";
    if (ifMatch != currentETag)
    {
        return StatusCode(412, "Precondition Failed");
    }
    return Ok();
}
cURL
curl -X PUT https://api.example.com/data/1 \
  -H "If-Match: \"old-etag-value\"" \
  -d '{"status": "updated"}'

Raw HTTP Response Example

HTTP Response
HTTP/1.1 412 Precondition Failed
Date: Wed, 21 Oct 2026 07:28:00 GMT
Content-Type: application/json

{"error": "Resource modified by another transaction"}

Real-world Examples

Google Cloud Storage: Returns 412 when attempting to overwrite an object using an x-goog-if-generation-match header that does not match the current object generation.
GitHub API: Can return 412 for conditional updates to repositories or gists if the provided SHA or ETag is stale.
Elasticsearch: Returns 409 or 412 depending on the API when using optimistic concurrency control (sequence numbers and primary terms) and a conflict occurs.

Frequently Asked Questions

What is the difference between 409 Conflict and 412 Precondition Failed?

409 indicates a conflict with the current state of the resource (e.g., trying to create a user that already exists). 412 specifically means a conditional header like `If-Match` was evaluated and failed.

How do I resolve a 412 error?

You must fetch the resource again to obtain its current state and new ETag, merge any changes if necessary, and then retry your request with the updated ETag in the `If-Match` header.

Which HTTP headers can trigger a 412?

The primary headers are `If-Match`, `If-None-Match`, `If-Modified-Since`, and `If-Unmodified-Since`, though `If-None-Match` usually results in a 304 Not Modified rather than a 412 on GET requests.

Is it required to use ETags for PUT requests?

It is not strictly required by HTTP, but it is highly recommended for REST APIs to implement optimistic concurrency control and avoid lost updates.

Did You Know?

The 412 status code is the backbone of "optimistic concurrency control" in REST APIs, allowing systems to safely update resources without locking them.

If a client uses `If-None-Match: *` on a PUT request, it means "only create this resource if it doesn't already exist." If it does exist, the server returns 412.

Developer Tips

  • Always use strong ETags (enclosed in quotes) for strict validation, as weak ETags (prefixed with W/) do not guarantee byte-for-byte equivalence.
  • When returning a 412, it is helpful to include the current ETag of the resource in the response headers so the client knows what the valid state is.
  • Ensure your frontend application gracefully handles 412s by prompting the user to review the conflicting changes before forcing an overwrite.

Interview Questions

These are questions you might face in a backend or API design interview that touch on HTTP 412.

Explain optimistic concurrency control using HTTP headers.

Optimistic concurrency control assumes conflicts are rare. A client fetches a resource and receives an ETag. To update it, the client sends a PUT with `If-Match: "etag"`. If another client modified the resource, the server's ETag will be different. The server detects the mismatch, rejects the request with a 412 Precondition Failed, and prevents the lost update.

When would an `If-None-Match` header result in a 412 instead of a 304?

In a GET or HEAD request, `If-None-Match` on an unmodified resource yields a 304 Not Modified. However, in a state-changing request like PUT or POST, it indicates "only perform this action if the resource does NOT match this ETag." If it does match, the precondition fails, returning a 412.

Common Interview Mistakes

  • Confusing 412 with 400 Bad Request. 412 is specifically for conditional request headers, whereas 400 is for malformed syntax.
  • Stating that 412 is used for authentication failures (which would be 401 Unauthorized or 403 Forbidden).
  • Failing to mention ETags or conditional headers (`If-Match`, `If-Unmodified-Since`) when explaining the code.