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
- 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.
- If the value comes from JSON/API input, validate the field type at the boundary before passing it into random_compat functions.
- Call the public API random_bytes($n) / random_int($a,$b) instead of internal RandomCompat_* helpers; those take ints and handle conversion themselves.
- 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
- Always is_string()-check values from JSON, DB, or config before byte-level helpers.
- Never rely on PHP's implicit string coercion for binary data.
- Use the public random_bytes()/random_int() API instead of RandomCompat_* internals.
- Enable strict_types=1 so coercion bugs surface at your own boundary first.
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
- RandomCompat_substr(): First argument should be a string
- RandomCompat_substr(): Second argument should be an integer
- RandomCompat_substr(): Third argument should be an integer…
- Expected an integer.
- random_bytes(): $bytes must be an integer
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 intView on GitHub (pinned to b5d188cc9d)