- Lunar Core version:
2.x-dev (c0c6a81f4c9cc429fb99a8b9c4030828e1f29c18)
- Lunar Filament version:
2.x-dev (1acfb414d026786b951471841891db4aa1e54b76)
- Lunar Admin version:
2.x-dev (9da7134fc7b8b27cc2f73e6cdb447d96caaa7fa5)
- Laravel version:
12.64.0
- Filament version:
5.6.8
- PHP version:
8.5
- Database driver: MySQL, although the error occurs before database access
Expected Behaviour
Laravel's production optimization commands should complete successfully:
php artisan config:cache
# or
php artisan optimize
Actual Behaviour
config:cache fails because a closure is stored in the Laravel configuration repository:
LogicException
Your configuration files could not be serialized because the value at
"lunar-filament.record_urls.order" is non-serializable.
The underlying exception is:
Call to undefined method Closure::__set_state()
This also causes deployments that run php artisan optimize to fail before the application can become healthy.
Steps To Reproduce
-
Install the Lunar v2 packages from 2.x-dev, including lunarphp/filament and lunarphp/lunar.
-
Clear any existing configuration cache:
-
Cache the configuration:
-
Observe the serialization exception for:
lunar-filament.record_urls.order
Root Cause
Lunar\Admin\LunarPanelProvider::registerBridgeRecordUrls() writes closures into Laravel's configuration repository:
$this->app['config']->set(
'lunar-filament.record_urls.order',
fn ($record, array $context = []) => ManageOrder::getUrl([
...$context,
'record' => $record,
]),
);
The same method also registers closures for:
lunar-filament.record_urls.product_variant
lunar-filament.record_urls.collection_edit
Laravel's config:cache command exports the final configuration using var_export(). Closures cannot be reconstructed from that exported
representation, so the first resolver causes the command to fail.
Relevant source:
Suggested Fix
Use serializable callable arrays instead of closures, for example:
$this->app['config']->set(
'lunar-filament.record_urls.order',
[RecordUrlResolvers::class, 'order'],
);
Alternatively, the URL resolver callbacks could be stored in a dedicated runtime registry rather than Laravel's configuration repository.
Lunar\Filament\Support\RecordUrls already checks is_callable(), so static callable arrays work without changing its public behavior.
Application Workaround
We worked around the issue by replacing the three closures after Lunar's service providers have booted:
app()->booted(function (): void {
config([
'lunar-filament.record_urls.order' => [
LunarRecordUrls::class,
'order',
],
'lunar-filament.record_urls.product_variant' => [
LunarRecordUrls::class,
'productVariant',
],
'lunar-filament.record_urls.collection_edit' => [
LunarRecordUrls::class,
'collectionEdit',
],
]);
});
LunarRecordUrls contains equivalent public static methods that call the existing Lunar Admin pages and resources.
With this workaround:
php artisan config:cache succeeds.
- All three values remain callable.
- The generated order, product-variant, and collection URLs remain unchanged.
2.x-dev(c0c6a81f4c9cc429fb99a8b9c4030828e1f29c18)2.x-dev(1acfb414d026786b951471841891db4aa1e54b76)2.x-dev(9da7134fc7b8b27cc2f73e6cdb447d96caaa7fa5)12.64.05.6.88.5Expected Behaviour
Laravel's production optimization commands should complete successfully:
php artisan config:cache # or php artisan optimizeActual Behaviour
config:cachefails because a closure is stored in the Laravel configuration repository:The underlying exception is:
This also causes deployments that run
php artisan optimizeto fail before the application can become healthy.Steps To Reproduce
Install the Lunar v2 packages from
2.x-dev, includinglunarphp/filamentandlunarphp/lunar.Clear any existing configuration cache:
Cache the configuration:
Observe the serialization exception for:
Root Cause
Lunar\Admin\LunarPanelProvider::registerBridgeRecordUrls()writes closures into Laravel's configuration repository:The same method also registers closures for:
Laravel's
config:cachecommand exports the final configuration usingvar_export(). Closures cannot be reconstructed from that exportedrepresentation, so the first resolver causes the command to fail.
Relevant source:
Suggested Fix
Use serializable callable arrays instead of closures, for example:
Alternatively, the URL resolver callbacks could be stored in a dedicated runtime registry rather than Laravel's configuration repository.
Lunar\Filament\Support\RecordUrlsalready checksis_callable(), so static callable arrays work without changing its public behavior.Application Workaround
We worked around the issue by replacing the three closures after Lunar's service providers have booted:
LunarRecordUrlscontains equivalent public static methods that call the existing Lunar Admin pages and resources.With this workaround:
php artisan config:cachesucceeds.