quarkusio/quarkus · error · java.lang.IllegalArgumentException
Not a formattable date/time object:
Error message
Not a formattable date/time object:
What it means
The Qute time template extensions (formatDate/formatTime/formatDateTime via {#format} or time namespace) convert the given value before formatting. If the value is neither a temporal type (TemporalAccessor) nor a Number epoch-milli, the extension throws this IllegalArgumentException because it cannot render the object as a date/time.
Source
Thrown at extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/extensions/TimeTemplateExtensions.java:75
return format(getFormattableObject(dateTimeObject, timeZone), pattern, locale, timeZone);
}
private static TemporalAccessor getFormattableObject(Object value,
ZoneId timeZone) {
if (value instanceof TemporalAccessor) {
return (TemporalAccessor) value;
} else if (value instanceof Date) {
return LocalDateTime.ofInstant(((Date) value).toInstant(),
timeZone);
} else if (value instanceof Calendar) {
return LocalDateTime.ofInstant(((Calendar) value).toInstant(),
timeZone);
} else if (value instanceof Number) {
return LocalDateTime.ofInstant(
Instant.ofEpochMilli(((Number) value).longValue()),
timeZone);
} else {
throw new IllegalArgumentException("Not a formattable date/time object: " + value);
}
}
private static DateTimeFormatter formatterForKey(Key key) {
DateTimeFormatter formatter;
DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
builder.appendPattern(key.pattern);
if (key.locale != null) {
formatter = builder.toFormatter(key.locale);
} else {
formatter = builder.toFormatter();
}
return key.timeZone != null ? formatter.withZone(key.timeZone) : formatter;
}
static final class Key {
private final String pattern;View on GitHub (pinned to e1c734241f)
Solutions
- Pass a supported type: TemporalAccessor (LocalDate/LocalDateTime/ZonedDateTime/Instant...) or a Number of epoch milliseconds.
- Parse String dates before rendering, or expose a getter returning a temporal type on the data model.
- Check for property-name typos so the correct date field is resolved.
- Note epoch values must be in milliseconds — convert seconds with Instant.ofEpochSecond(...) in your model.
Example fix
// before
data.put("created", "2024-01-01");
// after
data.put("created", LocalDate.of(2024, 1, 1)); // or Instant.ofEpochMilli(ms) Defensive patterns
Strategy: type-guard
Validate before calling
if (!(value instanceof TemporalAccessor) && !(value instanceof Number)) {
throw new IllegalArgumentException("Value not formattable as date/time: " + value);
} Type guard
boolean isFormattable(Object v) {
return v instanceof TemporalAccessor || v instanceof Number;
} Try / catch
try { engine.render(tpl, data); } catch (IllegalArgumentException e) { if (e.getMessage().startsWith("Not a formattable date/time object")) { /* fix model value type */ } else throw e; } Prevention
- Expose temporal types (LocalDate/Instant/...) in data models
- Parse String dates before rendering
- Ensure epoch values are milliseconds, not seconds
- Verify template property names resolve to the intended field
When it happens
Trigger: Calling {#format myValue format="..."} (or time.format) with a value that is a String, Instant-like unsupported wrapper, null object, or arbitrary POJO instead of a date/time type or epoch millis Number.
Common situations: Passing a String date (e.g. "2024-01-01") from config/JSON without parsing it first; passing a wrong model property due to a name typo (resolves to a different bean); passing java.util.Date-like values that were converted or a Long representing seconds (not millis).
Related errors
- No suitable template variant found
- Class [<rawClassName>] used in the parameter declaration in
- #cache cannot be used without the 'quarkus-cache' extension
- Template source not available
- ${exception}
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/e01f6b88c9a3fa96.
Report an issue: GitHub.