beemdevelopment/Aegis · error

Bad secret

Error message

Bad secret

What it means

The 'secret' parameter was present but could not be decoded: Base32 decoding failed (for otpauth) or Hex decoding failed (for motp), raising an EncodingException that is wrapped in GoogleAuthInfoException with message 'Bad secret'.

Solutions

  1. Confirm the secret is valid Base32 (A-Z, 2-7, '=' padding) for otpauth URIs, or hex for motp
  2. Re-copy the secret from the provider, removing spaces and newlines
  3. Use a Base32/Hex library to validate the secret before building the URI

Example fix

// before
String uri = "otpauth://totp/x?secret=not_base32!!";
// after
String secret = "JBSWY3DPEHPK3PXP"; // valid Base32
String uri = "otpauth://totp/x?secret=" + secret;
Defensive patterns

Strategy: validation

Validate before calling

if (!secret.matches("[A-Z2-7=]+")) throw new IllegalArgumentException("secret is not valid Base32");

Try / catch

try { return GoogleAuthInfo.parseUri(uri); } catch (GoogleAuthInfoException e) { if (e.getMessage().contains("Bad secret")) showBadSecretHint(); return null; }

Prevention

When it happens

Trigger: parseUri(Uri) with a secret containing characters outside the Base32 alphabet (or non-hex chars for motp), wrong casing/padding, or a URL-encoded secret not properly decoded.

Common situations: Manually typing a secret with O/0 or 1/I confusion, copying a hex secret into a totp URI, truncating the secret, or whitespace/newline contamination from copy-paste.

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/b66d9a8c100b918c. Report an issue: GitHub.

Appendix: source

Thrown at app/src/main/java/com/beemdevelopment/aegis/otp/GoogleAuthInfo.java:60

    }

    public static GoogleAuthInfo parseUri(Uri uri) throws GoogleAuthInfoException {
        String scheme = uri.getScheme();
        if (scheme == null || !(scheme.equals(SCHEME) || scheme.equals(MotpInfo.SCHEME))) {
            throw new GoogleAuthInfoException(uri, String.format("Unsupported protocol: %s", scheme));
        }

        // 'secret' is a required parameter
        String encodedSecret = uri.getQueryParameter("secret");
        if (encodedSecret == null) {
            throw new GoogleAuthInfoException(uri, "Parameter 'secret' is not present");
        }

        byte[] secret;
        try {
            secret = (scheme.equals(MotpInfo.SCHEME)) ? Hex.decode(encodedSecret) : parseSecret(encodedSecret);
        } catch (EncodingException e) {
            throw new GoogleAuthInfoException(uri, "Bad secret", e);
        }
        if (secret.length == 0) {
            throw new GoogleAuthInfoException(uri, "Secret is empty");
        }

        OtpInfo info;
        String issuer = "";
        try {
            String type = (scheme.equals(MotpInfo.SCHEME)) ? MotpInfo.ID : uri.getHost();
            if (type == null) {
                throw new GoogleAuthInfoException(uri, String.format("Host not present in URI: %s", uri.toString()));
            }

            switch (type) {
                case "totp":
                    TotpInfo totpInfo = new TotpInfo(secret);
                    String period = uri.getQueryParameter("period");
                    if (period != null) {

View on GitHub (pinned to d6f4e5925a)