ellite/Wallos · error · InvalidArgumentException
Invalid "counter" parameter.
Error message
Invalid "counter" parameter.
What it means
HOTP::getCounter reads the 'counter' parameter and throws InvalidArgumentException unless it is an int >= 0. The library enforces that the stored HOTP counter is a non-negative integer before use in URI generation or verification.
Solutions
- Set a valid counter before use: $hotp->setParameter('counter', 0); or pass counter in HOTP::create().
- Cast values when loading from external storage: (int) $row['counter'] before storing.
- When parsing a provisioning URI, ensure it includes counter=<int> in the query string.
- Guard with is_int($counter) && $counter >= 0 before calling verify()/getProvisioningUri().
Example fix
// before $hotp = HOTP::createFromSecret($secret); // counter never set $uri = $hotp->getProvisioningUri(); // throws // after $hotp = HOTP::create($secret, 0, 'sha1', 6); // counter provided $uri = $hotp->getProvisioningUri();
Defensive patterns
Strategy: validation
Validate before calling
$counter = $hotp->getParameter('counter');
if (!is_int($counter) || $counter < 0) {
$hotp->setParameter('counter', 0);
} Type guard
function hasValidCounter(object $hotp): bool {
$c = $hotp->getParameter('counter');
return is_int($c) && $c >= 0;
} Try / catch
try {
$uri = $hotp->getProvisioningUri();
} catch (InvalidArgumentException $e) {
$hotp->setParameter('counter', 0);
$uri = $hotp->getProvisioningUri();
} Prevention
- Always initialize the counter when constructing HOTP objects
- Cast counters from DB/JSON to int at the storage boundary
- Include counter= in any provisioning URI you generate for HOTP
- Add an integration test that round-trips HOTP provisioning URIs
When it happens
Trigger: Calling getCounter() (directly or via getProvisioningUri()/verify()) when the 'counter' parameter was never set, was set to a non-int, or was stored as a numeric string from parsing a provisioning URI query parameter.
Common situations: Creating an HOTP object without calling setParameter('counter', ...), loading a provisioning URI lacking a counter= query parameter, or counter arriving as string from JSON/DB storage.
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
- The counter must be at least 0.
- Counter must be at least 0.
- Label must not contain a colon.
- Invalid "digits" parameter.
- Unsupported " " OTP type
AI-assisted analysis of ellite/Wallos@52820e87ca (2026-09-13).
Data as JSON: /api/errors/b9f09c40102fd815.
Report an issue: GitHub.
Appendix: source
Thrown at libs/OTPHP/HOTP.php:55
$htop->setCounter(self::DEFAULT_COUNTER);
$htop->setDigest(self::DEFAULT_DIGEST);
$htop->setDigits(self::DEFAULT_DIGITS);
return $htop;
}
public static function generate(): self
{
return self::createFromSecret(self::generateSecret());
}
/**
* @return 0|positive-int
*/
public function getCounter(): int
{
$value = $this->getParameter('counter');
(is_int($value) && $value >= 0) || throw new InvalidArgumentException('Invalid "counter" parameter.');
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
{View on GitHub (pinned to 52820e87ca)