{"record":{"id":"1d9788273a5bf986","repo":"phalcon/cphalcon","slug":"wrong-key-in-paths-part","errorCode":null,"errorMessage":"Wrong key in paths: {part}","messagePattern":"Wrong key in paths: (.+?)","errorType":"exception","errorClass":"Phalcon\\Mvc\\Router\\Exceptions\\WrongPathsKey","httpStatus":null,"severity":"error","filePath":"phalcon/Mvc/Router.zep","lineNumber":1429,"sourceCode":"                        throw new BeforeMatchNotCallable();\n                    }\n\n                    if !{combinedBeforeMatch}(handledUri, combinedRoute, this) {\n                        continue;\n                    }\n                }\n\n                let combinedPaths      = combinedRoute->getPaths(),\n                    parts              = combinedPaths,\n                    matches            = combinedMatchesLocal,\n                    combinedConverters = combinedRoute->getConverters(),\n                    this->matches      = combinedMatchesLocal,\n                    this->matchedRoute = combinedRoute,\n                    routeFound         = true;\n\n                for combinedPart, combinedPosition in combinedPaths {\n                    if unlikely typeof combinedPart !== \"string\" {\n                        throw new WrongPathsKey(combinedPart);\n                    }\n\n                    if typeof combinedPosition !== \"string\" && typeof combinedPosition !== \"integer\" {\n                        continue;\n                    }\n\n                    if fetch combinedMatchPosition, combinedMatchesLocal[combinedPosition] {\n                        if typeof combinedConverters === \"array\" && fetch combinedConverter, combinedConverters[combinedPart] {\n                            let parts[combinedPart] = {combinedConverter}(combinedMatchPosition);\n                            continue;\n                        }\n\n                        let parts[combinedPart] = combinedMatchPosition;\n                    } else {\n                        if typeof combinedConverters === \"array\" && fetch combinedConverter, combinedConverters[combinedPart] {\n                            let parts[combinedPart] = {combinedConverter}(combinedPosition);\n                        } elseif typeof combinedPosition === \"integer\" {\n                            unset parts[combinedPart];","sourceCodeStart":1411,"sourceCodeEnd":1447,"githubUrl":"https://github.com/phalcon/cphalcon/blob/b7419de9cd0a8a3f48441ead84c9f8415d463e25/phalcon/Mvc/Router.zep#L1411-L1447","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n$router->add(\n    '/admin/:controller/:action/:params',\n    ['controller', 'action', 'params']\n);\n\n// after\n$router->add(\n    '/admin/:controller/:action/:params',\n    ['controller' => 1, 'action' => 2, 'params' => 3]\n);","handlingStrategy":"validation","validationCode":"// before registering routes, assert every paths array is a name => position map\nfunction assertValidPaths(array $paths): void\n{\n    foreach ($paths as $key => $_) {\n        if (!is_string($key)) {\n            throw new InvalidArgumentException(\n                'Route paths must use string keys, got key: ' . var_export($key, true)\n            );\n        }\n    }\n}\n\nforeach ($routesToRegister as $i => $def) {\n    assertValidPaths($def['paths'] ?? []);\n    $router->add($def['pattern'], $def['paths']);\n}","typeGuard":"function isValidPaths(mixed $paths): bool\n{\n    if (!is_array($paths)) {\n        return false;\n    }\n\n    foreach (array_keys($paths) as $key) {\n        if (!is_string($key)) {\n            return false;\n        }\n    }\n\n    return true;\n}","tryCatchPattern":"try {\n    $router->handle($uri);\n} catch (\\Phalcon\\Mvc\\Router\\Exceptions\\WrongPathsKey $e) {\n    $logger->error('Bad route paths definition: ' . $e->getMessage());\n    // fail the request explicitly; do not serve a half-matched route\n    $response->setStatusCode(500)->send();\n}","preventionTips":["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'"],"tags":["phalcon","router","route-paths","matching","validation"],"backgroundTag":"invalid-route-paths","analyzedSha":"b7419de9cd0a8a3f48441ead84c9f8415d463e25","analyzedAt":"2026-08-21T06:21:18.811Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}