beemdevelopment/Aegis · error · ParseException

Secret is not valid hexadecimal

Error message

Secret is not valid hexadecimal

What it means

EditEntryActivity.parseEntry() decodes the secret inside a try block catching EncodingException; on failure it builds the message 'Secret is not valid hexadecimal' for MOTP entries (or 'not valid base32' otherwise). This variant fires when a MOTP entry's secret is not a valid hex string, since Hex.decode rejects invalid characters or odd length.

Solutions

  1. Enter a valid hexadecimal string (0-9, A-F, even length) as the MOTP secret
  2. Check for lookalike characters (O vs 0, I vs 1) and correct them
  3. Confirm the entry type: if your secret is base32, switch the type to TOTP/HOTP instead of MOTP

Example fix

// before
secretField.setText("GXYZ"); // G, Y, Z not hex; odd-ish
// after
secretField.setText("0ABC");
Defensive patterns

Strategy: validation

Validate before calling

if (type.equals("motp") && !secret.matches("([0-9A-Fa-f]{2})+")) {
    _textSecretLayout.setError("MOTP secret must be valid hex (even length)");
    return;
}

Try / catch

try {
    saveEntry();
} catch (ParseException e) {
    if ("Secret is not valid hexadecimal".equals(e.getMessage())) {
        _textSecretLayout.setError("Enter a valid hex string");
    }
}

Prevention

When it happens

Trigger: Saving an entry with type MotpInfo.ID whose secret contains non-hex characters (G-Z) or has an odd number of characters, causing Hex.decode to throw EncodingException.

Common situations: Pasting a base32 secret into a MOTP entry, typos like 'O' vs '0', or copying a truncated/odd-length hex string from the provider.

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

Appendix: source

Thrown at app/src/main/java/com/beemdevelopment/aegis/ui/EditEntryActivity.java:757

        } catch (NumberFormatException e) {
            throw new ParseException("Digits is not an integer.");
        }

        byte[] secret;
        try {
            String secretString = new String(EditTextHelper.getEditTextChars(_textSecret));

            secret = (lowerCasedType.equals(MotpInfo.ID)) ?
                    Hex.decode(secretString) : GoogleAuthInfo.parseSecret(secretString);

            if (secret.length == 0) {
                throw new ParseException("Secret cannot be empty");
            }
        } catch (EncodingException e) {
            String exceptionMessage = (lowerCasedType.equals(MotpInfo.ID)) ?
                    "Secret is not valid hexadecimal" : "Secret is not valid base32.";

            throw new ParseException(exceptionMessage);
        }

        OtpInfo info;
        try {
            switch (type.toLowerCase(Locale.ROOT)) {
                case TotpInfo.ID:
                    info = new TotpInfo(secret, algo, digits, parsePeriod());
                    break;
                case SteamInfo.ID:
                    info = new SteamInfo(secret, algo, digits, parsePeriod());
                    break;
                case HotpInfo.ID:
                    long counter;
                    try {
                        counter = Long.parseLong(_textPeriodCounter.getText().toString());
                    } catch (NumberFormatException e) {
                        throw new ParseException("Counter is not an integer.");
                    }

View on GitHub (pinned to d6f4e5925a)