eclipse-vertx/vert.x · error · InputCoercionException
Expected an ISO 8601 formatted date time
Error message
Expected an ISO 8601 formatted date time
What it means
InstantDeserializer parses a JSON string into java.time.Instant and only accepts strings matching the ISO 8601 instant format (e.g. 2024-01-15T10:30:00Z). When the string cannot be parsed by DateTimeFormatter.ISO_INSTANT, it converts the DateTimeException into an InputCoercionException explaining that an ISO 8601 formatted date time was expected. It protects decode targets typed as Instant from silently accepting malformed timestamps.
Source
Thrown at vertx-core/src/main/java21/io/vertx/core/json/jackson/v3/InstantDeserializer.java:35
import java.time.DateTimeException;
import java.time.Instant;
import static java.time.format.DateTimeFormatter.ISO_INSTANT;
class InstantDeserializer extends StdDeserializer<Instant> {
InstantDeserializer() {
super(Instant.class);
}
@Override
public Instant deserialize(JsonParser p, DeserializationContext ctxt) {
String text = p.getString();
try {
return Instant.from(ISO_INSTANT.parse(text));
} catch (DateTimeException e) {
throw new InputCoercionException(p, "Expected an ISO 8601 formatted date time", p.currentToken(), Instant.class);
}
}
}
View on GitHub (pinned to fb308bd8c3)
Solutions
- Send timestamps in full ISO 8601 instant format, e.g. "2024-01-15T10:30:00Z" or with an explicit offset "2024-01-15T11:30:00+01:00"
- Normalize the value before decoding: append "Z" to date-only strings or convert epoch millis programmatically with Instant.ofEpochMilli
- If multiple formats must be supported, decode as String and parse with a custom DateTimeFormatter instead of binding directly to Instant
Example fix
// before
String in = "{\"ts\":\"2024-01-15\"}"; // InputCoercionException
Instant ts = Json.decodeValue(in, Wrapper.class).getTs();
// after
String in = "{\"ts\":\"2024-01-15T00:00:00Z\"}";
Instant ts = Json.decodeValue(in, Wrapper.class).getTs(); Defensive patterns
Strategy: validation
Validate before calling
static boolean isIsoInstant(String s) {
try { java.time.Instant.from(java.time.format.DateTimeFormatter.ISO_INSTANT.parse(s)); return true; }
catch (java.time.format.DateTimeParseException e) { return false; }
} Type guard
static java.time.Instant safeParse(String s) {
try { return java.time.Instant.parse(s); } catch (Exception e) { return null; }
} Try / catch
try {
Instant ts = Json.decodeValue(buf, Wrapper.class).getTs();
} catch (InputCoercionException | DecodeException e) {
log.error("timestamp is not ISO 8601: " + e.getMessage());
} Prevention
- Always emit timestamps with an explicit UTC designator 'Z' or numeric offset
- Avoid sending epoch millis or date-only strings where Instant fields are expected
- Add schema/format validation at API boundaries before decoding
When it happens
Trigger: Decoding JSON into an Instant field or Json.decodeValue target where the JSON string is not strict ISO 8601 instant format: e.g. "2024-01-15" (date only), "2024-01-15T10:30:00" (no offset), "2024-01-15 10:30:00Z" (space separator), epoch millis like "1705309800000", or locale-formatted dates.
Common situations: Frontend sends a date without the trailing Z or offset; legacy systems send timestamps with a space instead of 'T'; APIs send epoch milliseconds as a string; local date/time values produced by JavaScript toISOString-less formatting.
Related errors
- Unexpected trailing token
- Expecting the current parser token to be the start of an obj
- Expecting the current parser token to be the start of an arr
- Mapping
- Failed to decode
AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06).
Data as JSON: /api/errors/548d5c5c6ec99bae.
Report an issue: GitHub.