phalcon/cphalcon · error · Phalcon\Mvc\Router\Exception

Router cache is missing 'version' field

Error message

Router cache is missing 'version' field

What it means

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.

Solutions

  1. Regenerate the cache from the live router: call dumpDispatcher($path) (or let useCache repopulate) so the file is produced by buildDispatcherDump() itself
  2. If loading from a cache backend, clear the stale key before switching to router caching
  3. Treat cache files as build artifacts: delete and rebuild on any caching-related error instead of patching them
  4. If you must pre-validate, check isset($dump['version']) before calling loadDispatcherFromArray()

Example fix

// before
$router->loadDispatcher('/tmp/routes.cache.php'); // file lacks 'version' -> throws

// after: rebuild the cache from the configured router, then load
if (!file_exists($path)) {
    buildRoutes($router)->dumpDispatcher($path); // writes valid versioned dump
}
$router->loadDispatcher($path);
Defensive patterns

Strategy: validation

Validate before calling

// Only hand validated arrays to the loader
$dump = is_file($path) ? require $path : null;
if (!is_array($dump) || !isset($dump['version'])) {
    $dump = null; // treat as miss
}
if ($dump !== null) {
    $router->loadDispatcherFromArray($dump);
} else {
    buildRoutes($router)->dumpDispatcher($path);
}

Type guard

function isRouterDumpShape(mixed $dump): bool
{
    return is_array($dump) && isset($dump['version'], $dump['routes']) && is_array($dump['routes']);
}

Try / catch

try {
    $router->loadDispatcher($path);
} catch (\Phalcon\Mvc\Router\Exception $e) {
    @unlink($path);
    buildRoutes($router)->dumpDispatcher($path); // regenerate once, then load
    $router->loadDispatcher($path);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21). Data as JSON: /api/errors/748e22b67ad7379c. Report an issue: GitHub.

Appendix: source

Thrown at phalcon/Mvc/Router.zep:823

    }

    /**
     * Inverse of buildDispatcherDump(). Reconstructs every Route from the
     * scalar `routes` entries (preserving subclass and routeId), restores
     * every index, and marks the indexes clean so handle() skips rebuild.
     *
     * @throws \Phalcon\Mvc\Router\Exception
     */
    public function loadDispatcherFromArray(array dump) -> void
    {
        var routeData, route, routeClass, beforeMatch, converters,
            convName, converter, rebuiltRoutes, methodRoutesRehydrated,
            candidatesRehydrated, staticRehydrated, innerKey, innerVal,
            scalarIdx, scalarSubKey, mostInnerVal, mostInnerArr;
        int dumpVersion;

        if !isset dump["version"] {
            throw new Exception("Router cache is missing 'version' field");
        }

        let dumpVersion = (int) dump["version"];

        if dumpVersion !== 1 {
            throw new Exception(
                "Router cache version " . dumpVersion . " is not supported (this build supports version 1)"
            );
        }

        if !isset dump["routes"] {
            throw new Exception("Router cache is missing 'routes' field");
        }

        let rebuiltRoutes = [];

        for routeData in dump["routes"] {
            let routeClass = routeData["class"];

View on GitHub (pinned to b7419de9cd)