symfony/polyfill-mbstring · error · ValueError
Argument #1 ($substitute_character) must be "none", "long"…
Error message
Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint
What it means
Mbstring::mb_substitute_character() throws this ValueError when $substitute_character is not 'none', 'long', 'entity', or a valid integer codepoint. On PHP < 8.0 the polyfill returns false instead of throwing.
Solutions
- Pass exactly 'none', 'long', or 'entity' (string), or a valid integer codepoint such as 63 for '?'
- Convert numeric strings to int before calling: (int) $value
- Whitelist/normalize config values before applying them
- Catch \ValueError on PHP 8+ or check the false return on PHP < 8
Example fix
// before
mb_substitute_character('?');
// after
mb_substitute_character(63); // integer codepoint for '?' Defensive patterns
Strategy: validation
Validate before calling
$allowed = ['none','long','entity'];
if (!in_array($c, $allowed, true) && !is_int($c)) {
$c = is_numeric($c) ? (int) $c : 'none';
}
mb_substitute_character($c); Type guard
function isValidSubstituteCharacter($c): bool
{
return is_int($c) || in_array($c, ['none','long','entity'], true);
} Try / catch
try {
mb_substitute_character($c);
} catch (\ValueError $e) {
mb_substitute_character('none');
} Prevention
- Only pass 'none','long','entity' or an int codepoint — never a string form char
- Convert numeric strings from config to int
- Whitelist config values before applying
- Note behavior differs below PHP 8 (false return, no throw)
When it happens
Trigger: Calling mb_substitute_character('invalid') or with a string other than the three accepted keywords, e.g. mb_substitute_character('?') — a common mistake since the native function once accepted form characters; passing a numeric string like '63' instead of the int 63.
Common situations: Config files storing the substitute char as a string like '?' or '63'; porting code from PHP < 8 where mb_substitute_character accepted a string form char; validating HTTP Accept-Charset derived settings.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Argument #1 ($encoding) must be a valid encoding
- Argument #1 ($language) must be a valid language
- Argument #2 ($length) must be greater than 0
- mb_str_pad(): Argument #3 ($pad_string) must be a non-empty…
- mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT…
AI-assisted analysis of symfony/polyfill-mbstring@d3d318bad5 (2026-09-13).
Data as JSON: /api/errors/9cb51aaa51622c50.
Report an issue: GitHub.
Appendix: source
Thrown at Mbstring.php:679
return self::mb_convert_case($s, \MB_CASE_UPPER, $encoding);
}
public static function mb_substitute_character($c = null)
{
if (null === $c) {
return 'none';
}
if (0 === strcasecmp($c, 'none')) {
return true;
}
if (80000 > \PHP_VERSION_ID) {
return false;
}
if (\is_int($c) || 'long' === $c || 'entity' === $c) {
return false;
}
throw new \ValueError('Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint');
}
public static function mb_substr($s, $start, $length = null, $encoding = null)
{
$encoding = self::getEncoding($encoding);
if ('CP850' === $encoding || 'ASCII' === $encoding) {
return (string) substr($s, $start, null === $length ? 2147483647 : $length);
}
if ($start < 0) {
$start = iconv_strlen($s, $encoding) + $start;
if ($start < 0) {
$start = 0;
}
}
if (null === $length) {
$length = 2147483647;View on GitHub (pinned to d3d318bad5)