keycloak/keycloak · warning · PolicyValidationException
Unable not parse a date using format [{}]
Error message
Unable not parse a date using format [{}] What it means
Thrown as a PolicyValidationException (HTTP 400) by TimePolicyProviderFactory.validateFormat() when the 'notBefore' date string cannot be parsed using the format 'yyyy-MM-dd HH:mm:ss'. validateFormat() is called during policy create and update (onCreate/onUpdate) when both notBefore and notOnOrAfter are non-null. Note: the message contains a typo ('Unable not parse' should be 'Unable to parse').
Source
Thrown at authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java:168
config.compute("year", (s, s2) -> representation.getYear() != null ? representation.getYear() : null);
config.compute("yearEnd", (s, s2) -> representation.getYearEnd() != null ? representation.getYearEnd() : null);
config.compute("hour", (s, s2) -> representation.getHour() != null ? representation.getHour() : null);
config.compute("hourEnd", (s, s2) -> representation.getHourEnd() != null ? representation.getHourEnd() : null);
config.compute("minute", (s, s2) -> representation.getMinute() != null ? representation.getMinute() : null);
config.compute("minuteEnd", (s, s2) -> representation.getMinuteEnd() != null ? representation.getMinuteEnd() : null);
policy.setConfig(config);
}
private void validateFormat(String notBefore, String notOnOrAfter) {
Date nbf, noa;
try {
nbf = new SimpleDateFormat(TimePolicyProvider.DEFAULT_DATE_PATTERN).parse(TimePolicyProvider.format(notBefore));
} catch (Exception e) {
throw new PolicyValidationException("Unable not parse a date using format [" + notBefore + "]");
}
try {
noa = new SimpleDateFormat(TimePolicyProvider.DEFAULT_DATE_PATTERN).parse(TimePolicyProvider.format(notOnOrAfter));
} catch (Exception e) {
throw new PolicyValidationException("Unable not parse a date using format [" + notOnOrAfter + "]");
}
if (noa.before(nbf)) {
throw new PolicyValidationException("Expire time can't be set to a date before start time");
}
}
}
View on GitHub (pinned to 66c7e15a37)
Solutions
- Format notBefore as 'yyyy-MM-dd HH:mm:ss', e.g. '2024-01-15 00:00:00'. A bare date 'yyyy-MM-dd' is also accepted (auto-extended to midnight).
- If sending via API, use a date formatter in your client code to produce the exact format.
- Do not use 'T' separators or 'Z' suffixes — use a space between date and time.
- Validate the date string client-side before submitting.
Example fix
// before
rep.setNotBefore("2024/01/15T08:00:00Z");
// after
rep.setNotBefore("2024-01-15 08:00:00"); Defensive patterns
Strategy: validation
Validate before calling
// Before creating/updating a time policy, validate notBefore format
String notBefore = rep.getNotBefore();
if (notBefore != null && notOnOrAfter != null) {
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
fmt.parse(notBefore.trim().length() == 10 ? notBefore.trim() + " 00:00:00" : notBefore);
} catch (ParseException e) {
throw new IllegalArgumentException(
"notBefore must be 'yyyy-MM-dd HH:mm:ss' or 'yyyy-MM-dd', got: " + notBefore);
}
} Try / catch
try {
policyResource.create(rep);
} catch (BadRequestException e) {
if (e.getMessage().contains("Unable not parse a date using format")) {
showError("Invalid date format. Use yyyy-MM-dd HH:mm:ss (e.g., 2024-01-15 00:00:00).");
} else {
throw e;
}
} Prevention
- Always format notBefore as 'yyyy-MM-dd HH:mm:ss' or 'yyyy-MM-dd'.
- Use a date formatter in client code rather than constructing strings manually.
- Do not use slashes, 'T' separators, or timezone suffixes in date strings.
When it happens
Trigger: POST or PUT to .../policy/time with a notBefore value that doesn't match 'yyyy-MM-dd HH:mm:ss' or 'yyyy-MM-dd'. Example: notBefore = '01/15/2024' or 'Jan 15 2024'.
Common situations: Client using a locale-specific date format (MM/DD/YYYY vs yyyy-MM-dd). Missing time component when the server expects full datetime. Timezone-annotated strings (ISO-8601 with 'T' or 'Z') that don't match the expected space-separated format. Copy-pasting dates from a spreadsheet in a different format.
Related errors
- Expire time can't be set to a date before start time
- Organization groups cannot be used. Only realm groups are al
- Role can't be specified multiple times - {}
- Could not evaluate time-based policy [{}].
- You must provide at least one group
AI-assisted analysis of keycloak/keycloak@66c7e15a37 (2026-08-14).
Data as JSON: /api/errors/088cb8df35864909.
Report an issue: GitHub.