ellite/Wallos · error · InvalidArgumentException
The counter must be at least 0.
Error message
The counter must be at least 0.
What it means
HOTP::verify throws InvalidArgumentException when the optional $counter argument is a negative int. Since verify() accepts null|0|positive-int, any negative value is rejected outright before verification proceeds.
Solutions
- Clamp the counter: $counter = max(0, $computedCounter); before calling verify().
- Omit the $counter argument (pass null) to verify at the object's current counter.
- Validate user-supplied counters: reject negatives at input boundary with a 4xx-style validation error.
- Check stored counter values in your database for corruption/negative values.
Example fix
// before $ok = $hotp->verify($otp, $storedCounter - $window); // after $ok = $hotp->verify($otp, max(0, $storedCounter - $window));
Defensive patterns
Strategy: validation
Validate before calling
if ($counter !== null && (!is_int($counter) || $counter < 0)) {
throw new DomainException('Counter must be a non-negative integer');
} Type guard
function isValidCounter(mixed $counter): bool {
return $counter === null || (is_int($counter) && $counter >= 0);
} Try / catch
try {
$valid = $hotp->verify($otp, $counter);
} catch (InvalidArgumentException $e) {
log_warning('Negative counter passed to verify', ['counter' => $counter]);
return false;
} Prevention
- Clamp window arithmetic with max(0, ...) before calling verify
- Validate user-supplied counters at the controller boundary
- Treat counters as unsigned integers in your schema
- Add unit tests for window-verification edge cases near 0
When it happens
Trigger: Calling $hotp->verify($otp, -1) or passing a negative counter computed at runtime (e.g. $counter = $current - $window going below 0), or a negative value from user input/DB.
Common situations: Implementing look-ahead window verification where code computes counter minus window without clamping at 0; restoring state from a database with corrupt negative counters.
Related errors
- Invalid "counter" parameter.
- Counter must be at least 0.
- Unsupported " " OTP type
- Invalid data.
- The label is not set.
AI-assisted analysis of ellite/Wallos@52820e87ca (2026-09-13).
Data as JSON: /api/errors/080bee885237d1bd.
Report an issue: GitHub.
Appendix: source
Thrown at libs/OTPHP/HOTP.php:74
return $value;
}
public function getProvisioningUri(): string
{
return $this->generateURI('hotp', [
'counter' => $this->getCounter(),
]);
}
/**
* If the counter is not provided, the OTP is verified at the actual counter.
*
* @param null|0|positive-int $counter
*/
public function verify(string $otp, null|int $counter = null, null|int $window = null): bool
{
$counter >= 0 || throw new InvalidArgumentException('The counter must be at least 0.');
if ($counter === null) {
$counter = $this->getCounter();
} elseif ($counter < $this->getCounter()) {
return false;
}
return $this->verifyOtpWithWindow($otp, $counter, $window);
}
public function setCounter(int $counter): void
{
$this->setParameter('counter', $counter);
}
/**
* @return array<non-empty-string, callable>
*/View on GitHub (pinned to 52820e87ca)