apereo/cas · error · AccountNotFoundException

OTP format is invalid

Error message

OTP format is invalid

What it means

YubiKeyAuthenticationHandler.doAuthentication() validates the OTP's shape with YubicoClient.isValidOTPFormat before contacting the YubiCloud validation service. A token that is not a syntactically valid YubiKey OTP (roughly a 44-character modhex string prefixed by the device public id) cannot be valid, so the handler throws AccountNotFoundException to signal a bad credential.

Solutions

  1. Tap the YubiKey to generate a fresh OTP rather than typing a value; confirm it is ~44 modhex characters with the registered device prefix.
  2. Fix client-side input issues (keyboard layout, mobile browser, truncation) that corrupt the token before submission.
  3. Ensure the YubiKey is registered to the user's account so format checks and account mapping line up.
  4. Trim whitespace and pass the raw modhex string unchanged to the validation endpoint.

Example fix

// before
String otp = request.getParameter("otp").trim().toUpperCase();
// after
String otp = request.getParameter("otp").trim();
if (!YubicoClient.isValidOTPFormat(otp)) {
    throw new AccountNotFoundException("OTP format is invalid");
}
Defensive patterns

Strategy: validation

Validate before calling

if (otp == null || !otp.matches("[cbdefghijklnrtuv]{44}")) { reject("OTP format is invalid"); } // modhex, ~44 chars

Type guard

boolean isValidYubiOtp(String otp) { return otp != null && otp.matches("[cbdefghijklnrtuv]{32,48}"); }

Try / catch

try {
    handler.authenticate(credential);
} catch (AccountNotFoundException e) {
    // invalid OTP shape: prompt the user to tap the YubiKey again
}

Prevention

When it happens

Trigger: A user submits an empty, truncated, or non-modhex token (or any arbitrary string) as the YubiKey OTP during multifactor authentication.

Common situations: Users typing a static password instead of tapping the key; keyboard layout or mobile-browser input mangling modhex characters; API clients passing malformed tokens; whitespace or truncated paste in the otp field.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/eed23a55dc09da52. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-yubikey-core/src/main/java/org/apereo/cas/adaptors/yubikey/YubiKeyAuthenticationHandler.java:81

    @Override
    public boolean supports(final Class<? extends Credential> clazz) {
        return YubiKeyCredential.class.isAssignableFrom(clazz);
    }

    @Override
    public boolean supports(final Credential credential) {
        return YubiKeyCredential.class.isAssignableFrom(credential.getClass());
    }

    @Override
    protected AuthenticationHandlerExecutionResult doAuthentication(final Credential credential, final Service service) throws GeneralSecurityException {
        val yubiKeyCredential = (YubiKeyCredential) credential;

        val otp = yubiKeyCredential.getToken();

        if (!YubicoClient.isValidOTPFormat(otp)) {
            LOGGER.debug("Invalid OTP format [{}]", otp);
            throw new AccountNotFoundException("OTP format is invalid");
        }

        val authentication = Objects.requireNonNull(WebUtils.getInProgressAuthentication(),
            "CAS has no reference to an authentication event to locate a principal");
        val principal = authentication.getPrincipal();
        val uid = principal.getId();
        val publicId = registry.getAccountValidator().getTokenPublicId(otp);
        if (!this.registry.isYubiKeyRegisteredFor(uid, publicId)) {
            LOGGER.debug("YubiKey public id [{}] is not registered for user [{}]", publicId, uid);
            throw new AccountNotFoundException("YubiKey id is not recognized in registry");
        }

        try {
            val response = this.client.verify(otp);
            val status = response.getStatus();
            if (status.compareTo(ResponseStatus.OK) == 0) {
                LOGGER.debug("YubiKey response status [{}] at [{}]", status, response.getTimestamp());
                return createHandlerResult(yubiKeyCredential, this.principalFactory.createPrincipal(uid));

View on GitHub (pinned to e7288fc434)