symfony/routing · error · LogicException

Cannot mix UTF-8 requirement with non-UTF-8 charset for…

Error message

Cannot mix UTF-8 requirement with non-UTF-8 charset for variable "%s" in pattern "%s".

What it means

Opposite of error 77: the route's "utf8" option is enabled, but a variable's requirement regex is not valid UTF-8 (preg_match('//u', $regexp) fails), so matching semantics would be inconsistent. compilePattern throws a LogicException naming the variable and pattern.

Solutions

  1. Fix the requirement string so it is valid UTF-8 (mb_check_encoding($regexp, 'UTF-8')).
  2. Re-save the config file with UTF-8 encoding.
  3. Simplify the requirement to an ASCII-safe pattern like [a-z0-9-]+ if unicode matching is unnecessary.
  4. Sanitize programmatically: mb_convert_encoding($requirement, 'UTF-8', 'ISO-8859-1').

Example fix

// before (requirement bytes are not valid UTF-8)
$route = new Route('/{slug}', options: ['utf8' => true], requirements: ["slug" => "caf\xE9"]);

// after
$route = new Route('/{slug}', options: ['utf8' => true], requirements: ['slug' => 'café|[a-z-]+']);
Defensive patterns

Strategy: validation

Validate before calling

if ($route->getOption('utf8')) {
    foreach ($route->getRequirements() as $name => $req) {
        if (!mb_check_encoding($req, 'UTF-8')) {
            throw new \RuntimeException("Requirement for '$name' is not valid UTF-8.");
        }
    }
}

Try / catch

try {
    (new RouteCompiler())->compile($route);
} catch (\LogicException $e) {
    // fix requirement encoding or drop the utf8 option, then retry
}

Prevention

When it happens

Trigger: Compiling a route with options ['utf8' => true] and a requirement containing invalid/non-UTF-8 bytes, e.g. ['slug' => "caf\xE9|"] or a Latin-1 encoded requirement from a config file.

Common situations: Requirements loaded from mis-encoded YAML/PHP config files; concatenating encoded user input into requirement strings; enabling utf8 on all routes globally while some legacy requirements are ASCII-escaped bytes.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of symfony/routing@83fa223250 (2026-09-14). Data as JSON: /api/errors/c5321fec3a19a132. Report an issue: GitHub.

Appendix: source

Thrown at RouteCompiler.php:194

                    preg_quote($defaultSeparator),
                    $defaultSeparator !== $nextSeparator && '' !== $nextSeparator ? preg_quote($nextSeparator) : ''
                );
                if (('' !== $nextSeparator && !preg_match('#^\{[\w\x80-\xFF]+\}#', $followingPattern)) || '' === $followingPattern) {
                    // When we have a separator, which is disallowed for the variable, we can optimize the regex with a possessive
                    // quantifier. This prevents useless backtracking of PCRE and improves performance by 20% for matching those patterns.
                    // Given the above example, there is no point in backtracking into {page} (that forbids the dot) when a dot must follow
                    // after it. This optimization cannot be applied when the next char is no real separator or when the next variable is
                    // directly adjacent, e.g. '/{x}{y}'.
                    $regexp .= '+';
                }
            } else {
                if (!preg_match('//u', $regexp)) {
                    $useUtf8 = false;
                } elseif (!$needsUtf8 && preg_match('/[\x80-\xFF]|(?<!\\\\)\\\\(?:\\\\\\\\)*+(?-i:X|[pP][\{CLMNPSZ]|x\{[A-Fa-f0-9]{3})/', $regexp)) {
                    throw new \LogicException(\sprintf('Cannot use UTF-8 route requirements without setting the "utf8" option for variable "%s" in pattern "%s".', $varName, $pattern));
                }
                if (!$useUtf8 && $needsUtf8) {
                    throw new \LogicException(\sprintf('Cannot mix UTF-8 requirement with non-UTF-8 charset for variable "%s" in pattern "%s".', $varName, $pattern));
                }
                $regexp = self::transformCapturingGroupsToNonCapturings($regexp);
            }

            if ($important) {
                $token = ['variable', $isSeparator ? $precedingChar : '', $regexp, $varName, false, true];
            } else {
                $token = ['variable', $isSeparator ? $precedingChar : '', $regexp, $varName];
            }

            $tokens[] = $token;
            $variables[] = $varName;
        }

        if ($pos < \strlen($pattern)) {
            $tokens[] = ['text', substr($pattern, $pos)];
        }

View on GitHub (pinned to 83fa223250)