symfony/routing · error · InvalidParameterException

Parameters for route

Error message

Parameters for route "%s" cannot contain a circular reference (in object of class "%s").

What it means

When serializing extra query parameters into the generated URL string, UrlGenerator casts nested objects to arrays using get_object_vars. If the same object appears twice (a circular reference / repeated object), an infinite recursion would occur, so InvalidParameterException is thrown naming the object's class.

Solutions

  1. Remove the circularly-referencing object from the parameters or clone the parts you need.
  2. Cast parameters to scalars/arrays explicitly before passing them (e.g. ->toArray() on the entity or DTO).
  3. Use an ID instead of the object reference in the query parameters.

Example fix

// before
$router->generate('list', ['q' => $entity]); // entity has circular relation
// after
$router->generate('list', ['q' => $entity->getId()]);
Defensive patterns

Strategy: type-guard

Validate before calling

function hasCircular(array $params): bool {
  $seen = [];
  array_walk_recursive($params, function ($v) use (&$seen, &$has) { if (is_object($v)) { $id = spl_object_id($v); if (isset($seen[$id])) { $has = true; } $seen[$id] = true; } });
  return $has ?? false;
}

Type guard

function allScalars(array $params): bool {
  $ok = true;
  array_walk_recursive($params, function ($v) use (&$ok) { if (!is_scalar($v) && null !== $v) { $ok = false; } });
  return $ok;
}

Try / catch

try { $url = $router->generate($name, $params); } catch (\Symfony\Component\Routing\Exception\InvalidParameterException $e) { $url = $router->generate($name, array_map('strval', $scalarParams)); }

Prevention

When it happens

Trigger: Passing a query parameter that references an object already passed (e.g. ['obj' => $o, 'nested' => ['o' => $o]]) or an object whose property graph is self-referential, when calling generate().

Common situations: Passing Doctrine entities with back-references (bidirectional relations) or models holding a parent pointer into generate()'s query array.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at Generator/UrlGenerator.php:292

                $schemeAuthority .= $host.$port;
            }
        }

        if (self::RELATIVE_PATH === $referenceType) {
            $url = self::getRelativePath($this->context->getPathInfo(), $url);
        } else {
            $url = $schemeAuthority.$this->context->getBaseUrl().$url;
        }

        // add a query string if needed
        $extra = array_udiff_assoc(array_diff_key($parameters, $variables), $defaults, static fn ($a, $b) => $a == $b ? 0 : 1);
        $extra = array_replace($defaultQuery, $extra, $queryParameters);

        $seen = [];
        array_walk_recursive($extra, $caster = static function (&$v) use (&$caster, &$seen, $name) {
            if (\is_object($v)) {
                if (isset($seen[$id = spl_object_id($v)])) {
                    throw new InvalidParameterException(\sprintf('Parameters for route "%s" cannot contain a circular reference (in object of class "%s").', $name, get_debug_type($v)));
                }
                if ($vars = get_object_vars($v)) {
                    $seen[$id] = true;
                    array_walk_recursive($vars, $caster);
                    unset($seen[$id]);
                    $v = $vars;
                } elseif ($v instanceof \Stringable) {
                    $v = (string) $v;
                } else {
                    $v = [];
                }
            }
        });

        // extract fragment
        $fragment = $defaults['_fragment'] ?? '';

        if (isset($extra['_fragment'])) {

View on GitHub (pinned to 83fa223250)