yiisoft/yii2 · error · InvalidArgumentException

Expected expected value to be a string,

Error message

Expected expected value to be a string, 

What it means

Thrown by yii\base\Security::compareString() when the $expected argument (the trusted/reference side of a timing-attack-safe comparison) is not a PHP string. The method wraps hash_equals(), which requires strings, so it guards both arguments with is_string() first. The message appends gettype() of the offending value, e.g. 'Expected expected value to be a string, NULL given.'

Source

Thrown at framework/base/Security.php:547

        // Form the prefix that specifies Blowfish (bcrypt) algorithm and cost parameter.
        $salt = sprintf('$2y$%02d$', $cost);
        // Append the random salt data in the required base64 format.
        $salt .= str_replace('+', '.', substr(base64_encode($rand), 0, 22));

        return $salt;
    }

    /**
     * Performs string comparison using timing attack resistant approach.
     * @see https://codereview.stackexchange.com/q/13512
     * @param string $expected string to compare.
     * @param string $actual user-supplied string.
     * @return bool whether strings are equal.
     */
    public function compareString($expected, $actual)
    {
        if (!is_string($expected)) {
            throw new InvalidArgumentException('Expected expected value to be a string, ' . gettype($expected) . ' given.');
        }

        if (!is_string($actual)) {
            throw new InvalidArgumentException('Expected actual value to be a string, ' . gettype($actual) . ' given.');
        }

        return hash_equals($expected, $actual);
    }

    /**
     * Masks a token to make it uncompressible.
     * Applies a random mask to the token and prepends the mask used to the result making the string always unique.
     * Used to mitigate BREACH attack by randomizing how token is outputted on each request.
     * @param string $token An unmasked token.
     * @return string A masked token.
     * @since 2.0.12
     */
    public function maskToken($token)

View on GitHub (pinned to 66f00d18a2)

Solutions

  1. Read the gettype() named in the message and trace where that $expected value comes from.
  2. Fix the source: correct the config key path, add a NOT NULL default to the column, or regenerate/seed the missing secret.
  3. If absence is legitimate, short-circuit before calling: treat a non-string expected value as an automatic mismatch instead of an exception.
  4. For nullable values use is_string($expected) && Yii::$app->security->compareString($expected, $actual).

Example fix

// before
$valid = Yii::$app->security->compareString($user->api_secret, $requestSecret);
// $user->api_secret is NULL for rows created before the column existed

// after
$valid = is_string($user->api_secret)
    && Yii::$app->security->compareString($user->api_secret, (string) $requestSecret);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!is_string($expected)) {
    // regenerate/refresh the secret, or treat as an automatic mismatch
    return false;
}
return Yii::$app->security->compareString($expected, $actual);

Type guard

function assertComparableSecret($value): string
{
    if (!is_string($value)) {
        throw new \InvalidArgumentException(
            'Expected secret to be a string, ' . gettype($value) . ' given.'
        );
    }
    return $value;
}

Prevention

When it happens

Trigger: Calling compareString(null, $token) when a secret read from config with a wrong key returns null; comparing against an empty DB column (NULL api_secret, deleted cookie validation key); passing an int/float constant or an array/object as $expected; a json_decode() or unserialize() result used directly as the reference value.

Common situations: API-key or HMAC verification where the stored secret is null for legacy rows; cookie/security key missing from the production config; fixtures or seeds that leave token columns null; refactoring a constant to a config value that is not actually defined in that environment.

Related errors


AI-assisted analysis of yiisoft/yii2@66f00d18a2 (2026-08-17). Data as JSON: /api/errors/a0a4b7597ec73a59. Report an issue: GitHub.