phalcon/cphalcon · error · Phalcon\Mvc\Router\Exceptions\WrongPathsKey
Wrong key in paths: {part}
Error message
Wrong key in paths: {part} What it means
Thrown during router matching (combined-regex fast path, which only runs when no events manager is attached and the route bucket has no hostname constraints) when a matched route's paths array contains a key that is not a string. Route paths must be an associative map of parameter name => capture position (e.g. ['controller' => 1, 'action' => 2]); a plain list array has integer keys, so the router cannot resolve parameter names and rejects it at match time, not at add() time.
Source
Thrown at phalcon/Mvc/Router.zep:1429
throw new BeforeMatchNotCallable();
}
if !{combinedBeforeMatch}(handledUri, combinedRoute, this) {
continue;
}
}
let combinedPaths = combinedRoute->getPaths(),
parts = combinedPaths,
matches = combinedMatchesLocal,
combinedConverters = combinedRoute->getConverters(),
this->matches = combinedMatchesLocal,
this->matchedRoute = combinedRoute,
routeFound = true;
for combinedPart, combinedPosition in combinedPaths {
if unlikely typeof combinedPart !== "string" {
throw new WrongPathsKey(combinedPart);
}
if typeof combinedPosition !== "string" && typeof combinedPosition !== "integer" {
continue;
}
if fetch combinedMatchPosition, combinedMatchesLocal[combinedPosition] {
if typeof combinedConverters === "array" && fetch combinedConverter, combinedConverters[combinedPart] {
let parts[combinedPart] = {combinedConverter}(combinedMatchPosition);
continue;
}
let parts[combinedPart] = combinedMatchPosition;
} else {
if typeof combinedConverters === "array" && fetch combinedConverter, combinedConverters[combinedPart] {
let parts[combinedPart] = {combinedConverter}(combinedPosition);
} elseif typeof combinedPosition === "integer" {
unset parts[combinedPart];View on GitHub (pinned to b7419de9cd)
Solutions
- Change the route's second argument to an associative array: ['controller' => 'posts', 'action' => 'show', 'id' => 1] where numeric VALUES are regex capture positions
- If paths come from config or JSON, ensure keys are quoted strings ("controller": ...) so they survive decoding as strings
- Audit every $router->add/addGet/addPost/... call or config 'paths' entry feeding this route and replace list syntax with a name => position map
- As a stopgap to surface the bug earlier (outside production), iterate your routes at boot and assert all array keys of getPaths() are strings
Example fix
// before
$router->add(
'/admin/:controller/:action/:params',
['controller', 'action', 'params']
);
// after
$router->add(
'/admin/:controller/:action/:params',
['controller' => 1, 'action' => 2, 'params' => 3]
); Defensive patterns
Strategy: validation
Validate before calling
// before registering routes, assert every paths array is a name => position map
function assertValidPaths(array $paths): void
{
foreach ($paths as $key => $_) {
if (!is_string($key)) {
throw new InvalidArgumentException(
'Route paths must use string keys, got key: ' . var_export($key, true)
);
}
}
}
foreach ($routesToRegister as $i => $def) {
assertValidPaths($def['paths'] ?? []);
$router->add($def['pattern'], $def['paths']);
} Type guard
function isValidPaths(mixed $paths): bool
{
if (!is_array($paths)) {
return false;
}
foreach (array_keys($paths) as $key) {
if (!is_string($key)) {
return false;
}
}
return true;
} Try / catch
try {
$router->handle($uri);
} catch (\Phalcon\Mvc\Router\Exceptions\WrongPathsKey $e) {
$logger->error('Bad route paths definition: ' . $e->getMessage());
// fail the request explicitly; do not serve a half-matched route
$response->setStatusCode(500)->send();
} Prevention
- Always write paths as ['param' => position] maps, never lists
- Add a boot-time loop over $router->getRoutes() checking getPaths() keys are strings so defects surface at startup, not under traffic
- Validate route config files in CI with a schema that requires string keys under 'paths'
When it happens
Trigger: Defining a route via $router->add('/api/users', ['controller', 'action']) (list instead of map) and then issuing a request that matches it; also paths built with array_values(), array_merge() into a list, or decoded from JSON that produced numeric keys. The throw happens inside Router::handle() only after the URI actually matches, so the defect can sit dormant until traffic hits the route.
Common situations: Copy-pasting a paths example but dropping the '=>' pairs; building paths dynamically from a loop that uses append ($paths[] = ...) instead of named keys; YAML/JSON route files where quotes were lost and keys became integers; refactoring from short string paths ('Posts::show') to arrays and using wrong syntax.
Related errors
- The not-found paths must be an array or string
- The route contains invalid paths
- loadFromConfig requires an array or Phalcon\Config\ConfigInt
- 'defaults' must be an array
- 'routes' must be an array
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/1d9788273a5bf986.
Report an issue: GitHub.