paragonie/random_compat · error · TypeError

RandomCompat_strlen() expects a string

Error message

RandomCompat_strlen() expects a string

What it means

random_compat's RandomCompat_strlen() is a byte-safe wrapper around mb_strlen($str, '8bit') used to guarantee binary-safe string length on PHP 5/7 builds where the multibyte extension behaves differently. Before measuring, it asserts that the argument is actually a PHP string; if not, it throws TypeError. This is defensive input validation because random_compat's public API (e.g. random_bytes(), RandomCompat_substr()) deals exclusively with binary strings and must never silently accept ints, null, objects, or arrays.

Solutions

  1. Cast or verify the value is a string before calling: is_string($s) || throw new InvalidArgumentException(); or $s = (string) $s only if coercion is safe.
  2. If the value comes from JSON/API input, validate the field type at the boundary before passing it into random_compat functions.
  3. Call the public API random_bytes($n) / random_int($a,$b) instead of internal RandomCompat_* helpers; those take ints and handle conversion themselves.
  4. Trace the caller (RandomCompat_substr) — the real bug is the first argument to RandomCompat_substr(), not strlen.

Example fix

// before
$len = RandomCompat_strlen($maybeInt);
// after
if (!is_string($maybeInt)) {
    throw new InvalidArgumentException('Expected a binary string');
}
$len = RandomCompat_strlen($maybeInt);
Defensive patterns

Strategy: type-guard

Validate before calling

function assertBinaryString($value): void {
    if (!is_string($value)) {
        throw new InvalidArgumentException(
            'Expected binary string, got ' . gettype($value)
        );
    }
}
assertBinaryString($input);

Type guard

function isBinaryString($value): bool {
    return is_string($value);
}
if (isBinaryString($input)) {
    $len = RandomCompat_strlen($input);
}

Try / catch

try {
    $len = RandomCompat_strlen($input);
} catch (TypeError $e) {
    // $input was not a string; log and recover or rethrow with context
    throw new InvalidArgumentException('RandomCompat_strlen requires a string', 0, $e);
}

Prevention

When it happens

Trigger: Calling RandomCompat_strlen() directly with a non-string (e.g. an int, null, float, bool, array, or object). Most commonly it happens indirectly: passing a non-string as the first argument to RandomCompat_substr(), which then calls RandomCompat_strlen($binary_string) at the '$length === null' branch or the '$start === RandomCompat_strlen(...)' consistency check before its own validation order catches some cases.

Common situations: Developers treating random_compat as a general string utility and passing int/float output of arithmetic (e.g. a length computed from an API), passing null from an unset variable or failed function return, decoding JSON where a field was expected to be a string but came back as a number or null, or calling RandomCompat_substr() with a non-string first argument so the nested RandomCompat_strlen() call explodes first.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of paragonie/random_compat@b5d188cc9d (2026-09-13). Data as JSON: /api/errors/de2bf04cc6a15e93. Report an issue: GitHub.

Appendix: source

Thrown at lib/byte_safe_strings.php:50

            &&
        ((int) ini_get('mbstring.func_overload')) & MB_OVERLOAD_STRING
    ) {
        /**
         * strlen() implementation that isn't brittle to mbstring.func_overload
         *
         * This version uses mb_strlen() in '8bit' mode to treat strings as raw
         * binary rather than UTF-8, ISO-8859-1, etc
         *
         * @param string $binary_string
         *
         * @throws TypeError
         *
         * @return int
         */
        function RandomCompat_strlen($binary_string)
        {
            if (!is_string($binary_string)) {
                throw new TypeError(
                    'RandomCompat_strlen() expects a string'
                );
            }

            return (int) mb_strlen($binary_string, '8bit');
        }

    } else {
        /**
         * strlen() implementation that isn't brittle to mbstring.func_overload
         *
         * This version just used the default strlen()
         *
         * @param string $binary_string
         *
         * @throws TypeError
         *
         * @return int

View on GitHub (pinned to b5d188cc9d)