yiisoft/yii2 · error · InvalidArgumentException
Password must be a string and cannot be empty.
Error message
Password must be a string and cannot be empty.
What it means
Security::validatePassword() verifies bcrypt hashes and first asserts that the password argument is a non-empty string; null, non-string values, or '' throw InvalidArgumentException before the hash format check and password_verify(). This is strictly about the password argument — a malformed $hash raises the separate 'Hash is invalid.' error instead.
Source
Thrown at framework/base/Security.php:492
if ($cost === null) {
$cost = $this->passwordHashCost;
}
return password_hash($password, PASSWORD_DEFAULT, ['cost' => $cost]);
}
/**
* Verifies a password against a hash.
* @param string $password The password to verify.
* @param string $hash The hash to verify the password against.
* @return bool whether the password is correct.
* @throws InvalidArgumentException on bad password/hash parameters.
* @see generatePasswordHash()
*/
public function validatePassword($password, $hash)
{
if (!is_string($password) || $password === '') {
throw new InvalidArgumentException('Password must be a string and cannot be empty.');
}
if (
!preg_match('/^\$2[axy]\$(\d\d)\$[\.\/0-9A-Za-z]{22}/', $hash, $matches)
|| $matches[1] < 4
|| $matches[1] > 30
) {
throw new InvalidArgumentException('Hash is invalid.');
}
return password_verify($password, $hash);
}
/**
* Generates a salt that can be used to generate a password hash.
*
* The PHP [crypt()](https://www.php.net/manual/en/function.crypt.php) built-in function
* requires, for the Blowfish hash algorithm, a salt string in a specific format:View on GitHub (pinned to 66f00d18a2)
Solutions
- Handle empty/missing passwords as a failed verification in the caller before invoking Security
- Cast genuinely scalar input and bail on emptiness: $password = (string) $input; if ($password === '') return false;
- Mark the password attribute required+string in your Model rules so bad input dies at form validation, not inside Security
- For API input, reject non-string/empty password fields with a 4xx response at the boundary
Example fix
// before $ok = Yii::$app->security->validatePassword($model->password ?? null, $user->password_hash); // null password → InvalidArgumentException // after $password = $model->password ?? ''; $ok = $password !== '' && Yii::$app->security->validatePassword($password, $user->password_hash);
Defensive patterns
Strategy: validation
Validate before calling
if (!is_string($password) || $password === '') {
return false; // empty/missing password is a failed verification, not an exception
}
return Yii::$app->security->validatePassword($password, $hash); Type guard
function isNonEmptyPassword($password): bool
{
return is_string($password) && $password !== '';
} Try / catch
try {
$ok = Yii::$app->security->validatePassword($password, $hash);
} catch (\InvalidArgumentException $e) {
// empty/non-string password or invalid hash — treat as failed login and log the argument type, never the value
$ok = false;
} Prevention
- Make the login form's password attribute required + string so bad input dies at model validation
- Null-coalesce nullable attributes to '' and short-circuit before calling Security
- Reject non-string or empty password fields with 4xx at API boundaries
When it happens
Trigger: validatePassword(null, $hash) when the source attribute is nullable and null-coalesced incorrectly; an optional password field routed straight from request data with '' after trimming; array/scalar-mistyped input forwarded from a wrapper; test fixtures using empty strings.
Common situations: Login or token-verification endpoints that skip their own empty-input validation and call Security directly; nullable database columns feeding the call; API clients omitting the field so it decodes to null.
Related errors
- Hash is invalid.
- First parameter ($length) must be greater than 0
- Cost must be between 4 and 31.
- Encryption requires the OpenSSL PHP extension
- {cipher} is not an allowed cipher
AI-assisted analysis of yiisoft/yii2@66f00d18a2 (2026-08-17).
Data as JSON: /api/errors/775664e10036fd45.
Report an issue: GitHub.