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
- Tap the YubiKey to generate a fresh OTP rather than typing a value; confirm it is ~44 modhex characters with the registered device prefix.
- Fix client-side input issues (keyboard layout, mobile browser, truncation) that corrupt the token before submission.
- Ensure the YubiKey is registered to the user's account so format checks and account mapping line up.
- 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
- Prompt users to tap (not type) the YubiKey to emit the OTP.
- Trim whitespace and avoid transforms (uppercase, URL mangling) on the token.
- Check keyboard-layout/mobile-input issues corrupting modhex characters.
- Validate format client-side before submitting to CAS.
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
- Unable to authorize given token
- Failed to authenticate code
- cannot be found in the registry
- cannot reuse OTP
- Failed to authenticate code
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)