beemdevelopment/Aegis · error · OtpInfoException
Invalid Yandex secret length
Error message
Invalid Yandex secret length: %d bytes
What it means
YandexInfo.validateSecret requires the Yandex OTP secret to be exactly SECRET_LENGTH or SECRET_FULL_LENGTH bytes. QR-code secrets omit the checksum; the full form includes it. Any other length indicates corrupt or malformed secret data, so OtpInfoException is thrown.
Solutions
- Re-scan or regenerate the Yandex QR code so the secret is complete
- Verify the secret's encoding (base32 vs raw bytes) and decode it correctly before passing to parseSecret/validateSecret
- Strip whitespace/padding from the encoded secret and confirm the decoded byte length equals SECRET_LENGTH or SECRET_FULL_LENGTH
- Catch OtpInfoException around parseSecret and reject the entry with a clear message
Example fix
// before
byte[] secret = Base32.decode(secretString); // length may be wrong
YandexInfo.validateSecret(secret);
// after
byte[] secret = Base32.decode(secretString.replaceAll("\\s", ""));
if (secret.length != YandexInfo.SECRET_LENGTH && secret.length != YandexInfo.SECRET_FULL_LENGTH) {
throw new IllegalArgumentException("Expected " + YandexInfo.SECRET_LENGTH + " or "
+ YandexInfo.SECRET_FULL_LENGTH + " bytes, got " + secret.length);
}
YandexInfo.validateSecret(secret); Defensive patterns
Strategy: validation
Validate before calling
byte[] secret = Base32.decode(secretString.trim());
if (secret.length != YandexInfo.SECRET_LENGTH && secret.length != YandexInfo.SECRET_FULL_LENGTH) {
throw new IllegalArgumentException("Yandex secret must be "
+ YandexInfo.SECRET_LENGTH + " or " + YandexInfo.SECRET_FULL_LENGTH + " bytes");
} Try / catch
try {
YandexInfo.validateSecret(secret);
} catch (OtpInfoException e) {
Toast.makeText(ctx, "Corrupt Yandex secret: re-scan the QR code", Toast.LENGTH_LONG).show();
} Prevention
- Always decode base32 with whitespace stripped and correct padding
- Confirm the byte length before calling parseSecret/validateSecret
- Re-scan QR codes at original resolution; avoid screenshots/recompression
- Do not confuse hex and base32 encodings when exporting from other tools
When it happens
Trigger: Calling YandexInfo.validateSecret(byte[]) (via parseSecret) with a secret byte array whose length matches neither constant — e.g. base32-decoding errors, truncated QR payload, wrong key parameter in a yandex:// or otpauth URI.
Common situations: Importing Yandex Key entries from QR codes that were cropped or re-encoded, base32 strings containing whitespace/padding mishandled, exporting secrets from other tools with different encodings (hex vs base32 confusion).
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
AI-assisted analysis of beemdevelopment/Aegis@d6f4e5925a (2026-09-08).
Data as JSON: /api/errors/9ed06cbd37864333.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/main/java/com/beemdevelopment/aegis/otp/YandexInfo.java:112
public static byte[] parseSecret(byte[] secret) throws OtpInfoException {
validateSecret(secret);
if (secret.length != SECRET_LENGTH) {
return Arrays.copyOfRange(secret, 0, SECRET_LENGTH);
}
return secret;
}
/**
* Java implementation of ChecksumIsValid
* From: https://github.com/norblik/KeeYaOtp/blob/188a1a99f13f82e4ef8df8a1b9b9351ba236e2a1/KeeYaOtp/Core/Secret.cs
* License: GPLv3+
*/
public static void validateSecret(byte[] secret) throws OtpInfoException {
if (secret.length != SECRET_LENGTH && secret.length != SECRET_FULL_LENGTH) {
throw new OtpInfoException(String.format("Invalid Yandex secret length: %d bytes", secret.length));
}
// Secrets originating from a QR code do not have a checksum, so we assume those are valid
if (secret.length == SECRET_LENGTH) {
return;
}
char originalChecksum = (char) ((secret[secret.length - 2] & 0x0F) << 8 | secret[secret.length - 1] & 0xff);
char accum = 0;
int accumBits = 0;
int inputTotalBitsAvailable = secret.length * 8 - 12;
int inputIndex = 0;
int inputBitsAvailable = 8;
while (inputTotalBitsAvailable > 0) {
int requiredBits = 13 - accumBits;View on GitHub (pinned to d6f4e5925a)