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

  1. Pass exactly 'none', 'long', or 'entity' (string), or a valid integer codepoint such as 63 for '?'
  2. Convert numeric strings to int before calling: (int) $value
  3. Whitelist/normalize config values before applying them
  4. 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

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


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)