symfony/routing · error · LogicException

Route pattern " " cannot reference variable name " " more…

Error message

Route pattern "%s" cannot reference variable name "%s" more than once.

What it means

A route pattern may reference the same variable name only once; duplicates would produce an ambiguous PCRE named subpattern and the parameter value would be unclear. compilePattern keeps an accumulating $variables list (host variables first, then path variables) and throws a LogicException when a variable appears again.

Solutions

  1. Rename one of the duplicated placeholders, e.g. /blog/{slug}/edit/{id}.
  2. Remove the redundant placeholder if both segments carry the same data.
  3. If host and path share a value, keep it in one place and use requirements/defaults for the other.

Example fix

// before
$route = new Route('/blog/{slug}/edit/{slug}');

// after
$route = new Route('/blog/{slug}/edit/{id}');
Defensive patterns

Strategy: validation

Validate before calling

$vars = [];
preg_match_all('/\{(!)?([^}]+)\}/', $route->getPath().' '.$route->getHost(), $m);
foreach ($m[2] as $var) {
    if (in_array($var, $vars, true)) {
        throw new \InvalidArgumentException("Variable '$var' is used more than once.");
    }
    $vars[] = $var;
}

Try / catch

try {
    (new RouteCompiler())->compile($route);
} catch (\LogicException $e) {
    // deduplicate/rename placeholders before registering the route
}

Prevention

When it happens

Trigger: Compiling a route like /blog/{slug}/edit/{slug}, or a route whose host and path both contain the same variable, e.g. host '{subdomain}.example.com' with path '/{subdomain}/page'.

Common situations: Copy-pasting path segments and forgetting to rename the placeholder; templated route generation that appends the same parameter twice; combining host and path parameters with the same name.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at RouteCompiler.php:150

            $pos = $match[0][1] + \strlen($match[0][0]);

            if (!\strlen($precedingText)) {
                $precedingChar = '';
            } elseif ($useUtf8) {
                preg_match('/.$/u', $precedingText, $precedingChar);
                $precedingChar = $precedingChar[0];
            } else {
                $precedingChar = substr($precedingText, -1);
            }
            $isSeparator = '' !== $precedingChar && str_contains(static::SEPARATORS, $precedingChar);

            // A PCRE subpattern name must start with a non-digit. Also a PHP variable cannot start with a digit so the
            // variable would not be usable as a Controller action argument.
            if (preg_match('/^\d/', $varName)) {
                throw new \DomainException(\sprintf('Variable name "%s" cannot start with a digit in route pattern "%s". Please use a different name.', $varName, $pattern));
            }
            if (\in_array($varName, $variables)) {
                throw new \LogicException(\sprintf('Route pattern "%s" cannot reference variable name "%s" more than once.', $pattern, $varName));
            }

            if (\strlen($varName) > self::VARIABLE_MAXIMUM_LENGTH) {
                throw new \DomainException(\sprintf('Variable name "%s" cannot be longer than %d characters in route pattern "%s". Please use a shorter name.', $varName, self::VARIABLE_MAXIMUM_LENGTH, $pattern));
            }

            if ($isSeparator && $precedingText !== $precedingChar) {
                $tokens[] = ['text', substr($precedingText, 0, -\strlen($precedingChar))];
            } elseif (!$isSeparator && '' !== $precedingText) {
                $tokens[] = ['text', $precedingText];
            }

            $regexp = $route->getRequirement($varName);
            if (null === $regexp) {
                $followingPattern = substr($pattern, $pos);
                // Find the next static character after the variable that functions as a separator. By default, this separator and '/'
                // are disallowed for the variable. This default requirement makes sure that optional variables can be matched at all
                // and that the generating-matching-combination of URLs unambiguous, i.e. the params used for generating the URL are

View on GitHub (pinned to 83fa223250)