ellite/Wallos · error · InvalidArgumentException
Counter must be at least 0.
Error message
Counter must be at least 0.
What it means
HOTP::getParameterMap registers a normalizer for the 'counter' parameter that casts to int and throws InvalidArgumentException if the result is negative. This runs whenever a parameter is set or a provisioning URI is loaded, enforcing counter >= 0 at write time.
Solutions
- Fix the source value: ensure counter is >= 0 before setParameter() or in the provisioning URI.
- Clamp on import: max(0, (int) $value) when hydrating counters from external data.
- Sanitize URIs before loadFromProvisioningUri(): regex-check counter=\d+.
- Reject at your own input-validation layer with a clear user-facing message.
Example fix
// before
$hotp->setParameter('counter', $userInput); // could be -3
// after
$hotp->setParameter('counter', max(0, (int) $userInput)); Defensive patterns
Strategy: validation
Validate before calling
if ((int) $value < 0) {
throw new DomainException('Counter must be >= 0');
}
$hotp->setParameter('counter', (int) $value); Type guard
function sanitizeCounter(mixed $value): int {
return max(0, (int) $value);
} Try / catch
try {
$otp = $factory->loadFromProvisioningUri($uri);
} catch (InvalidArgumentException $e) {
log_error('Malformed counter in provisioning URI', ['uri' => $uri]);
throw new InvalidProvisioningUriException($uri, $e);
} Prevention
- Sanitize counters imported from external systems before hydration
- Regex-validate counter=\d+ (no minus) in URIs before parsing
- Use unsigned DB columns for counters
- Reject negative counters at your API input layer
When it happens
Trigger: Setting the counter parameter with a negative value ($hotp->setParameter('counter', -5)) or loading a provisioning URI containing counter=-1, so the map callback throws during parameter hydration.
Common situations: Parsing malicious/malformed otpauth://hotp URIs; importing OTP state from external systems where counters were stored as signed or corrupted values.
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
- Invalid "counter" parameter.
- The counter must be at least 0.
- Invalid "algorithm" parameter.
- Unsupported " " OTP type
- Invalid data.
AI-assisted analysis of ellite/Wallos@52820e87ca (2026-09-13).
Data as JSON: /api/errors/beff630aa985f531.
Report an issue: GitHub.
Appendix: source
Thrown at libs/OTPHP/HOTP.php:98
}
return $this->verifyOtpWithWindow($otp, $counter, $window);
}
public function setCounter(int $counter): void
{
$this->setParameter('counter', $counter);
}
/**
* @return array<non-empty-string, callable>
*/
protected function getParameterMap(): array
{
return [...parent::getParameterMap(), ...[
'counter' => static function (mixed $value): int {
$value = (int) $value;
$value >= 0 || throw new InvalidArgumentException('Counter must be at least 0.');
return $value;
},
]];
}
private function updateCounter(int $counter): void
{
$this->setCounter($counter);
}
/**
* @param null|0|positive-int $window
*/
private function getWindow(null|int $window): int
{
return abs($window ?? self::DEFAULT_WINDOW);
}View on GitHub (pinned to 52820e87ca)