428 - Precondition Required
HTTP 428 Precondition Required means the origin requires conditional requests to prevent lost updates.
Last reviewed: February 12, 2026|Editorial standard: source-backed technical guidance
What Does Precondition Required Mean?
The origin refuses unconditional mutations and requires preconditions so concurrent updates do not silently overwrite newer resource state.
Common Causes
- -Server policy rejects unconditional updates.
- -Client omits If-Match or related conditional headers.
- -Optimistic locking contract is not implemented in client.
How to Fix Precondition Required
- 1Fetch current resource validators (for example ETag) and send conditional headers on every mutation.
- 2Implement optimistic concurrency logic in clients to retry only after state refresh.
- 3Reject unconditional write paths in SDKs so missing preconditions are caught before network send.
Step-by-Step Diagnosis for Precondition Required
- 1Capture failing mutation request and confirm required conditional headers are absent or invalid.
- 2Inspect origin policy that mandates conditional writes and its documented validator requirements.
- 3Trace client write paths to identify callers bypassing ETag/version fetch before mutate.
- 4Retest with correct preconditions and verify lost-update protection remains intact.
Conditional Write Policy and Validator Coverage
- -Verify endpoint policy requiring conditionals (example: PUT without `If-Match` rejected by design with 428).
- -Audit validator freshness logic (example: client reuses stale ETag from cache beyond allowed staleness window).
Client Concurrency Workflow Enforcement
- -Trace read-modify-write paths for missing precondition injection (example: mobile offline sync sends unconditional PATCH after reconnect).
- -Check SDK middleware behavior (example: bulk update helper omits conditional headers for batched operations).
Implementation Examples
curl -i -X PUT https://api.example.com/v1/resource/42 -H "Content-Type: application/json" -d "{"status":"approved"}"
# Response:
# HTTP/1.1 428 Precondition Required
# {"error":"Precondition Required","code":"428"}const response = await fetch('https://api.example.com/v1/resource/42', {
method: 'PUT',
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
body: JSON.stringify({ status: 'approved' })
});
if (response.status === 428) {
console.error('Handle 428 Precondition Required');
}import requests
response = requests.put(
'https://api.example.com/v1/resource/42',
headers={'Accept': 'application/json', 'Content-Type': 'application/json'},
json={'status': 'approved'}
)
if response.status_code == 428:
print('Handle 428 Precondition Required')How to Verify the Fix
- -Repeat mutations with valid preconditions and confirm 428 is cleared for compliant requests.
- -Run concurrent update tests to ensure unconditional requests are still blocked intentionally.
- -Validate conflict-resolution UX and retry flow for stale validator cases.
How to Prevent Recurrence
- -Adopt optimistic concurrency tokens and idempotency guards for mutating operations.
- -Serialize high-contention writes and enforce deterministic dependency ordering.
- -Monitor precondition and lock-related failure signals with actionable alerts.
Pro Tip
- -make conditional headers mandatory in generated SDK mutation methods so developers cannot accidentally send unconditional writes.
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 HTTP services. Always validate implementation details against official provider documentation before deploying to production.