RouteCacheCommand calls prepareForSerialization() directly on route instances returned from getFreshApplicationRoutes().
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.
Reproduction repository: https://github.com/hosni/laravel-optimize-command-bug
- 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- The app registers a custom optimize command via
AppServiceProvider:
$this->optimizes(
optimize: 'test:route-analysis',
key: 'route-analysis',
);- The
test:route-analysiscommand iterates all routes and analyzes them:
foreach (Route::getRoutes() as $route) {
dump($route->uri(), $route->getController(), $route->gatherMiddleware());
}- Run the command directly — it succeeds:
php artisan test:route-analysis- Run it as part of
optimize— it fails:
php artisan optimizeExpected: 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-analysisProposed 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.