beemdevelopment/Aegis · error

PIN must be set before generating an OTP

Error message

PIN must be set before generating an OTP

What it means

MotpInfo.getOtp requires a PIN to have been set (via a MotpInfo constructor or setPin) before it can generate an mOTP code. If _pin is still null it throws IllegalStateException, because the mOTP algorithm cannot produce a code without the PIN component.

Solutions

  1. Call setPin(...) (or use the constructor that takes a pin) before invoking getOtp.
  2. Guard with a null check: only call getOtp when the entry's PIN is configured.
  3. If the entry legitimately has no PIN, it is not a valid mOTP credential — fix the source data/import.

Example fix

// before
MotpInfo motp = new MotpInfo(secret, algorithm, digits, period);
String code = motp.getOtp(time); // throws

// after
MotpInfo motp = new MotpInfo(secret, algorithm, digits, period);
motp.setPin("1234");
String code = motp.getOtp(time);
Defensive patterns

Strategy: validation

Validate before calling

if (motpInfo.getPin() == null) {
    throw new IllegalStateException("configure the mOTP PIN before generating codes");
}

Try / catch

try {
    String code = motpInfo.getOtp(time);
} catch (IllegalStateException e) {
    // PIN never set; prompt the user to configure the PIN
    promptForPin();
}

Prevention

When it happens

Trigger: Calling getOtp(time) on a MotpInfo instance that was constructed without a PIN and whose setPin was never invoked — i.e. generating a code for an mOTP entry whose PIN was never initialized (only seen in tests like testMotpInfoOtp that build MotpInfo directly).

Common situations: Programmatic use of MotpInfo where the developer builds the object incrementally and calls getOtp before setPin; deserialization paths that skip PIN assignment; partially constructed entries.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at app/src/main/java/com/beemdevelopment/aegis/otp/MotpInfo.java:36

    public static final int PERIOD = 10;
    public static final int DIGITS = 6;

    private String _pin;

    public MotpInfo(@NonNull byte[] secret) throws OtpInfoException {
        this(secret, null);
    }

    public MotpInfo(byte[] secret, String pin) throws OtpInfoException {
        super(secret, ALGORITHM, DIGITS, PERIOD);
        setPin(pin);
    }

    @Override
    public String getOtp(long time) {
        if (_pin == null) {
            throw new IllegalStateException("PIN must be set before generating an OTP");
        }

        try {
            MOTP otp = MOTP.generateOTP(getSecret(), getAlgorithm(false), getDigits(), getPeriod(), getPin(), time);
            return otp.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String getTypeId() {
        return ID;
    }

    @Override
    public JSONObject toJson() {
        JSONObject result = super.toJson();

View on GitHub (pinned to d6f4e5925a)