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
- Convert the value to ISO 8601 (e.g. Instant.ofEpochMilli(...).toString() => '2023-09-29T15:06:40Z') before sending/decoding
- Change the field type to String or long and convert manually with Instant.ofEpochMilli/ofEpochSecond
- Register a custom Instant deserializer that detects epoch-millis or other formats
- 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
- Always serialize Instants with Instant.toString() / DateTimeFormatter.ISO_INSTANT
- Convert epoch-millis from external APIs with Instant.ofEpochMilli before mapping
- Keep Instant fields as String in DTOs when producers use non-ISO formats
- Add contract tests asserting timestamp format on API payloads
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
- Expected a base64 encoded byte array
- Expected a base64 encoded byte array
- workerPoolSize must be > 0
- Expected a base64 encoded byte array
- Expected a base64 encoded byte array
AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06).
Data as JSON: /api/errors/fb60c1af7347f3ae.
Report an issue: GitHub.