beemdevelopment/Aegis · error

Secret is empty

Error message

Secret is empty

What it means

OtpInfo.checkSecret is a shared guard used by concrete OTP implementations before generating a code. It throws OtpInfoException('Secret is empty') when getSecret() returns a zero-length byte array, since an OTP cannot be derived from an empty key.

Solutions

  1. Ensure a non-empty secret is set before generating OTPs: check secret.getBytes().length > 0 (or decode the base32 string and verify it isn't blank).
  2. Reject the entry at import/parse time if its secret is empty rather than storing it.
  3. If the entry data is valid elsewhere, re-provision the credential to obtain a real secret.

Example fix

// before
TotpInfo info = new TotpInfo(new byte[0]);
String code = info.getOtp(); // OtpInfoException: Secret is empty

// after
byte[] secret = Base32.decode(secretString);
if (secret.length == 0) {
    throw new IllegalArgumentException("secret must not be empty");
}
TotpInfo info = new TotpInfo(secret);
Defensive patterns

Strategy: validation

Validate before calling

if (secret == null || secret.length == 0) {
    throw new IllegalArgumentException("OTP secret must not be empty");
}

Type guard

boolean hasSecret(OtpInfo info) {
    return info.getSecret() != null && info.getSecret().length > 0;
}

Try / catch

try {
    String code = otpInfo.getOtp();
} catch (OtpInfoException e) {
    if ("Secret is empty".equals(e.getMessage())) {
        // mark entry as invalid and ask user to re-provision
    }
}

Prevention

When it happens

Trigger: Generating an OTP (getOtp) for any OtpInfo subclass whose secret bytes are empty — typically after constructing an OtpInfo with a zero-length secret, or after decoding an empty/blank base32 secret from a URI, QR code, or imported vault entry.

Common situations: Importing otpauth:// URIs or backup files where the secret parameter is missing or empty; scanning a partial QR code; entries corrupted during migration; tests constructing TotpInfo/HotpInfo with empty secrets.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of beemdevelopment/Aegis@d6f4e5925a (2026-09-08). Data as JSON: /api/errors/e0c46a6409377ee0. Report an issue: GitHub.

Appendix: source

Thrown at app/src/main/java/com/beemdevelopment/aegis/otp/OtpInfo.java:35

    private byte[] _secret;
    private String _algorithm;
    private int _digits;

    public OtpInfo(byte[] secret) throws OtpInfoException {
        this(secret, DEFAULT_ALGORITHM, DEFAULT_DIGITS);
    }

    public OtpInfo(byte[] secret, String algorithm, int digits) throws OtpInfoException {
        setSecret(secret);
        setAlgorithm(algorithm);
        setDigits(digits);
    }

    public abstract String getOtp() throws OtpInfoException;

    protected void checkSecret() throws OtpInfoException {
        if (getSecret().length == 0) {
            throw new OtpInfoException("Secret is empty");
        }
    }

    public abstract String getTypeId();

    public String getType() {
        return getTypeId().toUpperCase(Locale.ROOT);
    }

    public JSONObject toJson() {
        JSONObject obj = new JSONObject();

        try {
            obj.put("secret", Base32.encode(getSecret()));
            obj.put("algo", getAlgorithm(false));
            obj.put("digits", getDigits());
        } catch (JSONException e) {
            throw new RuntimeException(e);

View on GitHub (pinned to d6f4e5925a)