beemdevelopment/Aegis · error
bad counter
Error message
bad counter: %d
What it means
HotpInfo.setCounter validates that an HOTP counter is non-negative via isCounterValid before storing it. A negative counter cannot occur in valid HOTP operation, so OtpInfoException('bad counter: %d') is thrown to reject the invalid value.
Solutions
- Ensure the counter value passed to setCounter (or the counter parameter in the URI/JSON being imported) is >= 0.
- When importing, validate the counter with HotpInfo.isCounterValid(counter) before constructing/assigning.
- If incrementCounter overflows, reset or rotate the credential rather than persisting a wrapped negative counter.
Example fix
// before
hotp.setCounter(counterFromUri); // may be -1
// after
if (HotpInfo.isCounterValid(counterFromUri)) {
hotp.setCounter(counterFromUri);
} else {
throw new IllegalArgumentException("invalid counter in URI: " + counterFromUri);
} Defensive patterns
Strategy: validation
Validate before calling
if (!HotpInfo.isCounterValid(counter)) {
throw new IllegalArgumentException("counter must be >= 0: " + counter);
} Try / catch
try {
hotp.setCounter(counter);
} catch (OtpInfoException e) {
// invalid (negative/overflowed) counter; reset to 0 or reject the entry
throw new ImportException("invalid HOTP counter", e);
} Prevention
- Validate the counter field of every otpauth://hotp URI/backup entry at import time.
- Clamp or reject negative counters before persistence.
- Watch for incrementCounter overflow on long-lived credentials.
When it happens
Trigger: Calling setCounter(negativeValue); calling incrementCounter() when the counter is Long.MIN_VALUE or would overflow to negative (counter + 1 < 0); parseUri encountering an otpauth://hotp URI with a negative counter parameter.
Common situations: Importing a malformed otpauth URI or encrypted-backup JSON with a negative counter; integer overflow after an astronomically large number of HOTP increments; hand-editing entry data.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Counter is not an integer.
- Secret is empty
- Parameter 'counter' is not present
- Secret is empty
- unsupported algorithm
AI-assisted analysis of beemdevelopment/Aegis@d6f4e5925a (2026-09-08).
Data as JSON: /api/errors/30480a372f654efb.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/main/java/com/beemdevelopment/aegis/otp/HotpInfo.java:70
try {
obj.put("counter", getCounter());
} catch (JSONException e) {
throw new RuntimeException(e);
}
return obj;
}
public long getCounter() {
return _counter;
}
public static boolean isCounterValid(long counter) {
return counter >= 0;
}
public void setCounter(long counter) throws OtpInfoException {
if (!isCounterValid(counter)) {
throw new OtpInfoException(String.format("bad counter: %d", counter));
}
_counter = counter;
}
public void incrementCounter() throws OtpInfoException {
setCounter(getCounter() + 1);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof HotpInfo)) {
return false;
}
HotpInfo info = (HotpInfo) o;
return super.equals(o) && getCounter() == info.getCounter();
}
}View on GitHub (pinned to d6f4e5925a)