eclipse-vertx/vert.x · error

428 Precondition Required (RFC6585)

Error message

428 Precondition Required (RFC6585)

What it means

SC_PRECONDITION_REQUIRED is an HttpResponseExpectation constant for HTTP 428 (RFC6585). The server requires the request to be conditional — carrying a header such as If-Match or If-Unmodified-Since — to prevent lost-update races. Vert.x exposes it so clients can assert or handle this requirement.

Source

Thrown at vertx-core/src/main/java/io/vertx/core/http/HttpResponseExpectation.java:270

  /**
   * 424 Failed Dependency (WebDAV, RFC4918)
   */
  HttpResponseExpectation SC_FAILED_DEPENDENCY = status(424);

  /**
   * 425 Unordered Collection (WebDAV, RFC3648)
   */
  HttpResponseExpectation SC_UNORDERED_COLLECTION = status(425);

  /**
   * 426 Upgrade Required (RFC2817)
   */
  HttpResponseExpectation SC_UPGRADE_REQUIRED = status(426);

  /**
   * 428 Precondition Required (RFC6585)
   */
  HttpResponseExpectation SC_PRECONDITION_REQUIRED = status(428);

  /**
   * 429 Too Many Requests (RFC6585)
   */
  HttpResponseExpectation SC_TOO_MANY_REQUESTS = status(429);

  /**
   * 431 Request Header Fields Too Large (RFC6585)
   */
  HttpResponseExpectation SC_REQUEST_HEADER_FIELDS_TOO_LARGE = status(431);

  /**
   * Any 5XX server error
   */
  HttpResponseExpectation SC_SERVER_ERRORS = status(500, 600);

  /**
   * 500 Internal Server Error

View on GitHub (pinned to fb308bd8c3)

Solutions

  1. Fetch the resource's ETag (or Last-Modified) and resend with an If-Match (or If-Unmodified-Since) header.
  2. Handle a resulting 412 by re-reading the resource, merging changes, and retrying with the fresh ETag.
  3. Adopt conditional updates in the client as standard practice for mutable resources.
  4. Check for server upgrades that newly mandate conditional requests.

Example fix

// before
request.putHeader("If-Match", etag); // absent before
client.put(8080, "localhost", "/items/1").send(body);
// after
String etag = getResponse.headers().get("ETag");
client.put(8080, "localhost", "/items/1")
  .putHeader("If-Match", etag)
  .send(body);
Defensive patterns

Strategy: validation

Validate before calling

Objects.requireNonNull(etag, "ETag required for conditional update on /items");

Type guard

boolean is428(HttpClientResponse resp) { return resp.statusCode() == 428; }

Try / catch

put().onFailure(err -> {
  if (isCauseStatus(err, 428)) refetchEtagAndRetryWithIfMatch();
});

Prevention

When it happens

Trigger: Performing PUT/DELETE/PATCH without conditional headers against an optimistic-concurrency API that mandates If-Match/If-Unmodified-Since. Observed when an expectation chain expects success but the server replies 428.

Common situations: APIs enforcing optimistic locking introduced in a newer server version; clients that never set ETag-based conditional headers; concurrent editors overwriting each other before the policy was enforced.

Related errors


AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06). Data as JSON: /api/errors/940fbcb24025056d. Report an issue: GitHub.