eclipse-vertx/vert.x · error
412 Precondition Failed
Error message
412 Precondition Failed
What it means
SC_PRECONDITION_FAILED is the HttpResponseExpectation constant for HTTP 412 Precondition Failed. Vert.x supplies it for response validation; a 412 failing this expectation surfaces '412 Precondition Failed'. The server evaluated a request precondition header (If-Match, If-None-Match, If-Unmodified-Since) and it evaluated to false, so the operation was aborted to protect the resource state.
Source
Thrown at vertx-core/src/main/java/io/vertx/core/http/HttpResponseExpectation.java:210
/** * 409 Conflict */ HttpResponseExpectation SC_CONFLICT = status(409); /** * 410 Gone */ HttpResponseExpectation SC_GONE = status(410); /** * 411 Length Required */ HttpResponseExpectation SC_LENGTH_REQUIRED = status(411); /** * 412 Precondition Failed */ HttpResponseExpectation SC_PRECONDITION_FAILED = status(412); /** * 413 Request Entity Too Large */ HttpResponseExpectation SC_REQUEST_ENTITY_TOO_LARGE = status(413); /** * 414 Request-URI Too Long */ HttpResponseExpectation SC_REQUEST_URI_TOO_LONG = status(414); /** * 415 Unsupported Media Type */ HttpResponseExpectation SC_UNSUPPORTED_MEDIA_TYPE = status(415); /** * 416 Requested Range Not Satisfiable
View on GitHub (pinned to fb308bd8c3)
Solutions
- Re-fetch the resource to get the current ETag/Last-Modified, then resend the conditional request
- Drop the precondition header if your operation does not actually need state protection
- Merge/rebase your change on top of the current resource representation before resending
- Verify you are not caching and reusing stale ETags across requests
- If persistent, investigate concurrent writers and adopt server-side locking or versioned updates
Example fix
// before
request.putHeader("If-Match", cachedEtag).sendJson(body); // 412
// after
webClient.get(path).send()
.onSuccess(r -> {
String etag = r.getHeader("ETag");
webClient.put(path).putHeader("If-Match", etag).sendJson(body);
}); Defensive patterns
Strategy: retry
Validate before calling
// Fetch a fresh precondition value immediately before the write
String etag = awaitGet(path).getHeader("ETag");
if (etag == null || etag.isBlank()) { dropPreconditionOrRecheck(); } Type guard
boolean isPreconditionFailed(Throwable t) {
return t instanceof VertxHttpResponseException
&& ((VertxHttpResponseException) t).getResponse().statusCode() == 412;
} Try / catch
if (isPreconditionFailed(t)) { refetchEtagAndRetryOnce(); } else { throw t; } Prevention
- Never reuse cached ETags across write operations
- Keep the GET-to-write window short
- Use version numbers in payloads if the API offers it
- Expect 412 under concurrency and implement one refetch-rebase retry
When it happens
Trigger: Issuing conditional PUT/DELETE/PATCH via Vert.x WebClient with If-Match/If-None-Match/If-Unmodified-Since headers while the resource has changed since the precondition value was obtained (stale ETag or timestamp).
Common situations: Optimistic concurrency with ETags where another writer updated the resource between your GET and PUT, caching layers reusing old ETags, clock-skewed If-Unmodified-Since values, conditional GET caches (If-None-Match) invalidated server-side.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- 428 Precondition Required (RFC6585)
- size must be > 0
- maxExecuteTime must be > 0
- Unit must not be null
- Result is already complete
AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06).
Data as JSON: /api/errors/27154ca154cc5ab5.
Report an issue: GitHub.