PreconditionFailed
AWS S3 PreconditionFailed (HTTP 412) indicates that a conditional request was rejected because the object's current ETag or last-modified timestamp did not match the values specified in the If-Match or If-Unmodified-Since headers.
Last reviewed: March 28, 2026|Editorial standard: source-backed technical guidance
What Does Precondition Failed Mean?
PreconditionFailed is S3's way of saying: "The data has changed since you last looked at it." It prevents the "Lost Update" problem. When you send an If-Match header with an ETag, S3 ensures the object is exactly in the state you expect before performing the write. If another process modified the object in the millisecond between your read and write, S3 aborts your request to save you from corrupting the data.
Common Causes
- -Race Conditions: Two Lambda functions or users trying to update the same JSON config file at the exact same time.
- -Stale ETags: Using a cached ETag from a previous session that is no longer valid because the object was updated or replaced.
- -Incorrect Header Usage: Using
If-Unmodified-Sincewith a timestamp that is slightly off due to clock skew or aggressive caching. - -Creation Conflicts: Using
If-None-Match: *to ensure a file is new, but the file already exists.
How to Fix Precondition Failed
- 1Re-Fetch the ETag: Immediately call
HeadObjectto get the latest ETag and last-modified date. - 2Implement Retry-Loop: Catch the 412 error, pull the fresh data, merge your changes, and try the conditional write again.
- 3Prefer ETags over Timestamps: Always use
If-Matchwith ETags for higher precision than timestamp-based conditions. - 4Force Overwrite (If Safe): If you do not care about concurrent changes, simply remove the
If-Matchheader to perform a "blind write."
Step-by-Step Diagnosis for Precondition Failed
- 1Confirm which header failed (usually
If-Matchin the request logs). - 2Compare the ETag in your request with the one currently on the object using
aws s3api head-object. - 3Check CloudTrail for concurrent
PutObjectcalls to the same key from different IAM identities. - 4Verify if your application is caching ETags too aggressively (e.g., in a Redis or local memory store).
Optimistic Locking Workflow
- -Read: Client gets Object (ETag: "v1").
- -Modify: Client changes data locally.
- -Write: Client sends
PutObjectwithIf-Match: "v1". - -Outcome: If S3 ETag is still "v1", success. If "v2", 412 PreconditionFailed.
ETags in Multipart Uploads
- -Beware: S3 ETags for multipart uploads are NOT standard MD5 hashes. They represent a checksum of part checksums. Never try to calculate these locally; always read them from S3.
Implementation Examples
async function safeUpdate(bucket, key, newData) {
const head = await s3.send(new HeadObjectCommand({ Bucket: bucket, Key: key }));
try {
await s3.send(new PutObjectCommand({
Bucket: bucket,
Key: key,
Body: newData,
IfMatch: head.ETag // Only write if ETag hasn't changed
}));
} catch (err) {
if (err.name === 'PreconditionFailed') {
// Re-fetch and retry logic here
}
}
}aws s3api put-object --bucket my-bucket --key new-file.txt \
--body "hello" --if-none-match "*"
# Returns PreconditionFailed if new-file.txt already exists.How to Verify the Fix
- -Successfully perform a conditional
PutObjectafter a freshHeadObjectcall. - -Verify that the error only triggers when the ETag is intentionally changed.
- -Ensure your retry logic does not enter an infinite loop during high-contention periods.
How to Prevent Recurrence
- -Always Fetch Fresh: Call
HeadObjectimmediately before any conditional write. - -Use DynamoDB for Locks: For extremely high-contention objects, consider using DynamoDB's atomic counters or conditional expressions as a distributed lock manager.
- -Shorten Cache TTL: If you must cache ETags, keep the TTL extremely short (seconds, not minutes).
- -Pro-tip: Use
If-None-Match: *when you want to ensure you are creating a NEW object. This returns PreconditionFailed if the key is already taken, preventing accidental overwrites of existing files.
Decision Support
Compare Guide
409 Conflict vs 412 Precondition Failed: When to Use Each
Choose 412 when If-Match or If-Unmodified-Since checks fail; choose 409 for state conflicts without failed precondition headers during concurrent updates.
Playbook
Conflict and Concurrency Playbook (409 / 412 / OptimisticLock)
Use this playbook to separate true write conflicts from stale precondition failures, then apply safe re-fetch, optimistic-lock, and retry choices.
Official References
Provider Context
This guidance is specific to AWS services. Always validate implementation details against official provider documentation before deploying to production.