{"record":{"id":"748e22b67ad7379c","repo":"phalcon/cphalcon","slug":"router-cache-is-missing-version-field","errorCode":null,"errorMessage":"Router cache is missing 'version' field","messagePattern":"Router cache is missing 'version' field","errorType":"exception","errorClass":"Phalcon\\Mvc\\Router\\Exception","httpStatus":null,"severity":"error","filePath":"phalcon/Mvc/Router.zep","lineNumber":823,"sourceCode":"    }\n\n    /**\n     * Inverse of buildDispatcherDump(). Reconstructs every Route from the\n     * scalar `routes` entries (preserving subclass and routeId), restores\n     * every index, and marks the indexes clean so handle() skips rebuild.\n     *\n     * @throws \\Phalcon\\Mvc\\Router\\Exception\n     */\n    public function loadDispatcherFromArray(array dump) -> void\n    {\n        var routeData, route, routeClass, beforeMatch, converters,\n            convName, converter, rebuiltRoutes, methodRoutesRehydrated,\n            candidatesRehydrated, staticRehydrated, innerKey, innerVal,\n            scalarIdx, scalarSubKey, mostInnerVal, mostInnerArr;\n        int dumpVersion;\n\n        if !isset dump[\"version\"] {\n            throw new Exception(\"Router cache is missing 'version' field\");\n        }\n\n        let dumpVersion = (int) dump[\"version\"];\n\n        if dumpVersion !== 1 {\n            throw new Exception(\n                \"Router cache version \" . dumpVersion . \" is not supported (this build supports version 1)\"\n            );\n        }\n\n        if !isset dump[\"routes\"] {\n            throw new Exception(\"Router cache is missing 'routes' field\");\n        }\n\n        let rebuiltRoutes = [];\n\n        for routeData in dump[\"routes\"] {\n            let routeClass = routeData[\"class\"];","sourceCodeStart":805,"sourceCodeEnd":841,"githubUrl":"https://github.com/phalcon/cphalcon/blob/b7419de9cd0a8a3f48441ead84c9f8415d463e25/phalcon/Mvc/Router.zep#L805-L841","documentation":"loadDispatcherFromArray() (used by loadDispatcher() and cache-hit paths of useCache()) validates the dump's shape before restoring routes. The very first requirement is a 'version' key; an array without it is treated as corrupt or foreign and rejected immediately. Dumps are only meant to be produced by buildDispatcherDump() on the same Phalcon build.","triggerScenarios":"Passing a hand-built array to loadDispatcherFromArray(); loading a cache file that was truncated, half-written, or written by a different/older serializer; a cache file that returns some other structure entirely (config array, route definitions array) at the expected path; cache entries written under a key by unrelated code.","commonSituations":"Reusing a cache key or file path previously holding different data; a deploy wrote a partial file (killed mid-write before atomic rename); someone hand-edited the generated file; another subsystem stores its own array at the same cache key.","solutions":["Regenerate the cache from the live router: call dumpDispatcher($path) (or let useCache repopulate) so the file is produced by buildDispatcherDump() itself","If loading from a cache backend, clear the stale key before switching to router caching","Treat cache files as build artifacts: delete and rebuild on any caching-related error instead of patching them","If you must pre-validate, check isset($dump['version']) before calling loadDispatcherFromArray()"],"exampleFix":"// before\n$router->loadDispatcher('/tmp/routes.cache.php'); // file lacks 'version' -> throws\n\n// after: rebuild the cache from the configured router, then load\nif (!file_exists($path)) {\n    buildRoutes($router)->dumpDispatcher($path); // writes valid versioned dump\n}\n$router->loadDispatcher($path);","handlingStrategy":"validation","validationCode":"// Only hand validated arrays to the loader\n$dump = is_file($path) ? require $path : null;\nif (!is_array($dump) || !isset($dump['version'])) {\n    $dump = null; // treat as miss\n}\nif ($dump !== null) {\n    $router->loadDispatcherFromArray($dump);\n} else {\n    buildRoutes($router)->dumpDispatcher($path);\n}","typeGuard":"function isRouterDumpShape(mixed $dump): bool\n{\n    return is_array($dump) && isset($dump['version'], $dump['routes']) && is_array($dump['routes']);\n}","tryCatchPattern":"try {\n    $router->loadDispatcher($path);\n} catch (\\Phalcon\\Mvc\\Router\\Exception $e) {\n    @unlink($path);\n    buildRoutes($router)->dumpDispatcher($path); // regenerate once, then load\n    $router->loadDispatcher($path);\n}","preventionTips":["Never hand-craft dispatcher cache files; always produce them via dumpDispatcher()","Wrap cache loading in a regenerate-on-invalid helper so corruption self-heals","Use unique cache keys/paths so unrelated payloads never occupy the router slot"],"tags":["phalcon","router","caching","cache-validation"],"backgroundTag":"cache-schema-mismatch","analyzedSha":"b7419de9cd0a8a3f48441ead84c9f8415d463e25","analyzedAt":"2026-08-21T06:21:18.811Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}