cakephp/cakephp · error · MissingRouteException

A named route was found for

Error message

A named route was found for `%s`, but matching failed. Passed parameters: `%s`.

What it means

RouteCollection::match() found a route registered with the given `_name`, but Route::match() could not produce a URL from the passed parameters — the parameters don't satisfy the route's template placeholders, defaults, or passed-argument pattern. Cake throws MissingRouteException because URL generation failed even though the route name exists.

Solutions

  1. Print the route template (Router::getRouteCollection()->get($name)->template and its defaults/patterns) and compare against the exact parameters passed in the failing Router::url()/match() call
  2. Supply every required placeholder in the URL array and remove keys the route template does not use (plugin/prefix/controller/action are ignored for named routes)
  3. Check route pattern constraints — e.g. an id pattern of \d+ fails for string identifiers; relax the pattern or pass a valid value
  4. If the route shape changed, update the _name reference or connect a new route with that name and the expected parameters
  5. Catch MissingRouteException around URL generation if the link is optional and can be skipped

Example fix

// before
$url = Router::url(['_name' => 'articles:view', 'slug' => $article->slug]);
// route: /articles/view/:id with [['id', '\d+')]

// after
$url = Router::url(['_name' => 'articles:view', 'id' => $article->id]);
Defensive patterns

Strategy: try-catch

Validate before calling

$collection = Router::getRouteCollection();
$route = $collection->get($name);
if ($route === null) {
    throw new RuntimeException("Named route {$name} is not connected");
}
foreach ($route->options ?? [] as $key => $pattern) {
    if (isset($url[$key]) && !preg_match('/^' . $pattern . '$/', (string)$url[$key])) {
        throw new InvalidArgumentException("Parameter {$key} violates pattern {$pattern}");
    }
}

Type guard

function canGenerateUrl(string $name, array $url): bool {
    $route = Router::getRouteCollection()->get($name);
    return $route !== null && (bool)$route->match($url, []);
}

Try / catch

try {
    $url = Router::url(['_name' => 'articles:view', 'id' => $id]);
} catch (Cake\Routing\Exception\MissingRouteException $e) {
    Log::error('Named route match failed: ' . $e->getMessage());
    $url = '/';
}

Prevention

When it happens

Trigger: Calling Router::url(['_name' => 'route:name', ...]) (which ends up in RouteCollection::match) with parameters that don't match the named route's template: missing a required placeholder value, wrong key names, extra/insufficient passed arguments, or a value that fails the route's regex/pattern constraints.

Common situations: Renaming or reshaping a route template in routes.php without updating every Router::url() call referencing it by _name; typos in parameter keys; passing plugin/prefix keys to a route defined without them; a pattern constraint (e.g. [['id', '\d+']]) rejecting a non-numeric id like a string slug; test code (e.g. testPluginRoutes) generating URLs with stale parameters.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of cakephp/cakephp@1128eba9b0 (2026-09-12). Data as JSON: /api/errors/b3939b39256ed041. Report an issue: GitHub.

Appendix: source

Thrown at src/Routing/RouteCollection.php:298

    public function match(array $url, array $context): string
    {
        // Named routes support optimization.
        if (isset($url['_name'])) {
            $name = $url['_name'];
            unset($url['_name']);
            if (isset($this->_named[$name])) {
                $route = $this->_named[$name];
                $out = $route->match($url + $route->defaults, $context);
                if ($out) {
                    return $out;
                }
                $message = sprintf(
                    'A named route was found for `%s`, but matching failed. Passed parameters: `%s`.',
                    $name,
                    (string)json_encode($url),
                );

                throw new MissingRouteException([
                    'url' => $name,
                    'context' => $context,
                    // Escape `%` so the message survives CakeException's vsprintf() pass unchanged.
                    'message' => str_replace('%', '%%', $message),
                ]);
            }
            throw new MissingRouteException(['url' => $name, 'context' => $context]);
        }

        foreach ($this->_getNames($url) as $name) {
            if (empty($this->_routeTable[$name])) {
                continue;
            }
            foreach ($this->_routeTable[$name] as $route) {
                $match = $route->match($url, $context);
                if ($match) {
                    return $match === '/' ? $match : trim($match, '/');
                }

View on GitHub (pinned to 1128eba9b0)