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

  1. 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).
  2. If sending via API, use a date formatter in your client code to produce the exact format.
  3. Do not use 'T' separators or 'Z' suffixes — use a space between date and time.
  4. 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

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


AI-assisted analysis of keycloak/keycloak@66c7e15a37 (2026-08-14). Data as JSON: /api/errors/088cb8df35864909. Report an issue: GitHub.