{"record":{"id":"27154ca154cc5ab5","repo":"eclipse-vertx/vert.x","slug":"412-precondition-failed","errorCode":null,"errorMessage":"412 Precondition Failed","messagePattern":"412 Precondition Failed","errorType":"http","errorClass":null,"httpStatus":412,"severity":"error","filePath":"vertx-core/src/main/java/io/vertx/core/http/HttpResponseExpectation.java","lineNumber":210,"sourceCode":"  /**\n   * 409 Conflict\n   */\n  HttpResponseExpectation SC_CONFLICT = status(409);\n\n  /**\n   * 410 Gone\n   */\n  HttpResponseExpectation SC_GONE = status(410);\n\n  /**\n   * 411 Length Required\n   */\n  HttpResponseExpectation SC_LENGTH_REQUIRED = status(411);\n\n  /**\n   * 412 Precondition Failed\n   */\n  HttpResponseExpectation SC_PRECONDITION_FAILED = status(412);\n\n  /**\n   * 413 Request Entity Too Large\n   */\n  HttpResponseExpectation SC_REQUEST_ENTITY_TOO_LARGE = status(413);\n\n  /**\n   * 414 Request-URI Too Long\n   */\n  HttpResponseExpectation SC_REQUEST_URI_TOO_LONG = status(414);\n\n  /**\n   * 415 Unsupported Media Type\n   */\n  HttpResponseExpectation SC_UNSUPPORTED_MEDIA_TYPE = status(415);\n\n  /**\n   * 416 Requested Range Not Satisfiable","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/eclipse-vertx/vert.x/blob/fb308bd8c3f12c79f4ae89bef67fadf6c80d036e/vertx-core/src/main/java/io/vertx/core/http/HttpResponseExpectation.java#L192-L228","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\nrequest.putHeader(\"If-Match\", cachedEtag).sendJson(body); // 412\n// after\nwebClient.get(path).send()\n  .onSuccess(r -> {\n    String etag = r.getHeader(\"ETag\");\n    webClient.put(path).putHeader(\"If-Match\", etag).sendJson(body);\n  });","handlingStrategy":"retry","validationCode":"// Fetch a fresh precondition value immediately before the write\nString etag = awaitGet(path).getHeader(\"ETag\");\nif (etag == null || etag.isBlank()) { dropPreconditionOrRecheck(); }","typeGuard":"boolean isPreconditionFailed(Throwable t) {\n  return t instanceof VertxHttpResponseException\n    && ((VertxHttpResponseException) t).getResponse().statusCode() == 412;\n}","tryCatchPattern":"if (isPreconditionFailed(t)) { refetchEtagAndRetryOnce(); } else { throw t; }","preventionTips":["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"],"tags":["http","precondition","etag","concurrency"],"backgroundTag":"http-error-response","analyzedSha":"fb308bd8c3f12c79f4ae89bef67fadf6c80d036e","analyzedAt":"2026-09-06T11:37:12.241Z","contentChangedAt":"2026-09-06T11:37:12.241Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}