beemdevelopment/Aegis · error

Parameter 'counter' is not present

Error message

Parameter 'counter' is not present

What it means

Thrown by GoogleAuthInfo.parseUri(Uri) when a hotp-type URI lacks the required 'counter' query parameter. HOTP needs an initial counter value to derive codes; without it the OtpInfo cannot be initialized, so parsing aborts. It fires on otpauth://hotp URIs missing &counter=N.

Solutions

  1. Add &counter=N (initial counter, usually 0) to the otpauth://hotp URI
  2. Ensure the exporting service includes the counter for HOTP entries
  3. If the token is actually time-based, use otpauth://totp instead

Example fix

// before
String uri = "otpauth://hotp/x?secret=JBSWY3DPEHPK3PXP";
// after
String uri = "otpauth://hotp/x?secret=JBSWY3DPEHPK3PXP&counter=0";
Defensive patterns

Strategy: validation

Validate before calling

if (uri.getHost().equals("hotp") && uri.getQueryParameter("counter") == null) throw new IllegalArgumentException("hotp URI missing counter");

Try / catch

try { return GoogleAuthInfo.parseUri(uri); } catch (GoogleAuthInfoException e) { askUserForCounter(uri); return null; }

Prevention

When it happens

Trigger: parseUri(Uri) encountering otpauth://hotp/label?secret=... without counter=.

Common situations: QR generators defaulting to TOTP-style parameters for a HOTP secret, manually migrating a HOTP entry and forgetting the counter, or provider docs assuming the counter is optional.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

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

                    String period = uri.getQueryParameter("period");
                    if (period != null) {
                        totpInfo.setPeriod(Integer.parseInt(period));
                    }
                    info = totpInfo;
                    break;
                case "steam":
                    SteamInfo steamInfo = new SteamInfo(secret);
                    period = uri.getQueryParameter("period");
                    if (period != null) {
                        steamInfo.setPeriod(Integer.parseInt(period));
                    }
                    info = steamInfo;
                    break;
                case "hotp":
                    HotpInfo hotpInfo = new HotpInfo(secret);
                    String counter = uri.getQueryParameter("counter");
                    if (counter == null) {
                        throw new GoogleAuthInfoException(uri, "Parameter 'counter' is not present");
                    }
                    hotpInfo.setCounter(Long.parseLong(counter));
                    info = hotpInfo;
                    break;
                case YandexInfo.HOST_ID:
                    String pin = uri.getQueryParameter("pin");
                    if (pin != null) {
                        pin = new String(parseSecret(pin), StandardCharsets.UTF_8);
                    }

                    info = new YandexInfo(secret, pin);
                    issuer = info.getType();
                    break;
                case MotpInfo.ID:
                    info = new MotpInfo(secret);
                    break;
                default:
                    throw new GoogleAuthInfoException(uri, String.format("Unsupported OTP type: %s", type));

View on GitHub (pinned to d6f4e5925a)