412 🌐 HTTP

HTTP 412 Precondition Failed

A conditional request header (If-Match, If-Unmodified-Since) didn’t match the current state of the resource.

Seen on: REST API AWS Azure

Meaning

412 protects against lost updates: you sent If-Match: <etag> and someone else changed the resource since you read it. Object stores (S3, Azure Blob, GCS) and many REST APIs use it for optimistic concurrency.

Common causes

  • Resource modified by another client since your read (ETag changed)
  • Stale ETag cached by the client
  • Weak vs strong ETag mismatch (W/"...")
  • S3/Azure conditional writes (If-None-Match: * when the object already exists)

⚡ Quick fix

  1. Re-fetch the resource, re-apply your change, and retry with the new ETag
  2. Show users a “this item changed” message instead of overwriting
  3. Make sure proxies don’t rewrite ETags (gzip can turn strong ETags weak)

Detailed fix by platform

REST API

  1. Optimistic update loop:
    javascript
    async function update(id, change) {
      for (let attempt = 0; attempt < 3; attempt++) {
        const cur = await fetch(`/api/docs/${id}`);
        const etag = cur.headers.get('ETag');
        const doc = { ...(await cur.json()), ...change };
        const res = await fetch(`/api/docs/${id}`, { method: 'PUT', headers: { 'If-Match': etag, 'Content-Type': 'application/json' }, body: JSON.stringify(doc) });
        if (res.status !== 412) return res;
      }
      throw new Error('Document keeps changing — try again later');
    }

How to diagnose

  1. Condition — Which If-* header did you send?
  2. Current state — What is the current ETag/Last-Modified?
  3. Concurrency — Did another client write in between?

🧠 Still stuck? Analyze your error

Paste the full message, response headers or stack trace — we'll detect the platform and point to the most likely cause.