eclipse-vertx/vert.x · error · InvalidFormatException

Expected an ISO 8601 formatted date time

Error message

Expected an ISO 8601 formatted date time

What it means

InstantDeserializer is the Jackson deserializer Vert.x registers for java.time.Instant. It parses the string with DateTimeFormatter.ISO_INSTANT; if parsing throws DateTimeException, it rethrows as Jackson InvalidFormatException with message 'Expected an ISO 8601 formatted date time', so JSON decoding of a non-ISO-8601 timestamp fails.

Source

Thrown at vertx-core/src/main/java/io/vertx/core/json/jackson/InstantDeserializer.java:32

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;

import java.io.IOException;
import java.time.DateTimeException;
import java.time.Instant;

import static java.time.format.DateTimeFormatter.ISO_INSTANT;

class InstantDeserializer extends JsonDeserializer<Instant> {
  @Override
  public Instant deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
    String text = p.getText();
    try {
      return Instant.from(ISO_INSTANT.parse(text));
    } catch (DateTimeException e) {
      throw new InvalidFormatException(p, "Expected an ISO 8601 formatted date time", text, Instant.class);
    }
  }
}

View on GitHub (pinned to fb308bd8c3)

Solutions

  1. Convert the value to ISO 8601 (e.g. Instant.ofEpochMilli(...).toString() => '2023-09-29T15:06:40Z') before sending/decoding
  2. Change the field type to String or long and convert manually with Instant.ofEpochMilli/ofEpochSecond
  3. Register a custom Instant deserializer that detects epoch-millis or other formats
  4. Normalize producer-side formatting to DateTimeFormatter.ISO_INSTANT output

Example fix

// before
{"createdAt": 1696000000000} // InvalidFormatException: Expected an ISO 8601 formatted date time
// after
{"createdAt": "2023-09-29T15:06:40Z"}
// or in Java: instant.toString() when serializing
Defensive patterns

Strategy: validation

Validate before calling

public static boolean isIsoInstant(String s) {
  if (s == null) return false;
  try { Instant.from(ISO_INSTANT.parse(s)); return true; }
  catch (DateTimeParseException e) { return false; }
}

Type guard

static boolean isInstantShaped(Object v) {
  return v instanceof String s && s.matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})");
}

Try / catch

try {
  Config cfg = Json.decodeValue(buf, Config.class);
} catch (DecodeException e) {
  if (e.getCause() instanceof InvalidFormatException ife && ife.getTargetType() == Instant.class) {
    // normalize the timestamp then retry
  }
}

Prevention

When it happens

Trigger: Decoding JSON containing an Instant field whose value is not ISO 8601: epoch millis as a number-context string like '1696000000000', formats like '2024-01-01 10:00:00' (space instead of T, missing timezone), or locale-formatted dates.

Common situations: Consuming third-party API timestamps (epoch millis, RFC 1123) into Instant fields; database dumps with 'yyyy-MM-dd HH:mm:ss' values; payloads produced by non-Jackson serializers with different date conventions; frontend sending Date.toString().

Related errors


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