Skip to content

Repository files navigation

RouteCacheCommand calls prepareForSerialization() directly on route instances returned from getFreshApplicationRoutes().

https://github.com/laravel/framework/blob/3093ff3a61f88225f16fec35dda65e1e7c0867a4/src/Illuminate/Foundation/Console/RouteCacheCommand.php#L56-L64

Route::prepareForSerialization() intentionally removes runtime dependencies: https://github.com/laravel/framework/blob/3093ff3a61f88225f16fec35dda65e1e7c0867a4/src/Illuminate/Routing/Route.php#L1560

Those route objects remain referenced by the live router collection. Later in the same PHP process, any code that still needs to analyze routes can fail with:

LogicException: Route is not bound.

This happens because after container is unset.

Access like $route->container goes through __get():
https://github.com/laravel/framework/blob/3093ff3a61f88225f16fec35dda65e1e7c0867a4/src/Illuminate/Routing/Route.php#L1569-L1573

which calls parameters():
https://github.com/laravel/framework/blob/3093ff3a61f88225f16fec35dda65e1e7c0867a4/src/Illuminate/Routing/Route.php#L432-L435

And that throws when the route was never bound to a request:
https://github.com/laravel/framework/blob/3093ff3a61f88225f16fec35dda65e1e7c0867a4/src/Illuminate/Routing/Route.php#L483-L490

This is especially visible with php artisan optimize, which runs built-in cache commands before ServiceProvider::$optimizeCommands:

[
    'config' => 'config:cache',
    'events' => 'event:cache',
    'routes' => 'route:cache',
    'views' => 'view:cache',
    ...ServiceProvider::$optimizeCommands,
]

If a package registers an optimize task that analyzes routes (for example, to cache generated metadata), that task can fail after route:cache has already run in the same optimize command.

Image

Reproduction repository: https://github.com/hosni/laravel-optimize-command-bug

  1. Clone the reproduction repository:
git clone https://github.com/hosni/laravel-optimize-command-bug.git
cd laravel-optimize-command-bug
composer install
cp .env.example .env
php artisan key:generate
  1. The app registers a custom optimize command via AppServiceProvider:
$this->optimizes(
    optimize: 'test:route-analysis',
    key: 'route-analysis',
);
  1. The test:route-analysis command iterates all routes and analyzes them:
foreach (Route::getRoutes() as $route) {
    dump($route->uri(), $route->getController(), $route->gatherMiddleware());
}
  1. Run the command directly — it succeeds:
php artisan test:route-analysis
  1. Run it as part of optimize — it fails:
php artisan optimize

Expected: optimize completes successfully, including the route-analysis task.

Actual: The route-analysis task fails with:

LogicException: Route is not bound.

The failure occurs on any route when gatherMiddleware() is called after route:cache has already executed in the same process.

Workaround:

php artisan optimize --except=route-analysis
php artisan test:route-analysis

Proposed fix: Clone each route before calling prepareForSerialization(), and build the route cache file from the cloned/prepared collection so live route instances are not mutated.

About

Minimal reproduction for a Laravel bug where `route:cache` mutates live route instances during `php artisan optimize`, breaking custom optimize tasks that analyze routes.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages