From 0c51b675e61b483bbaf5f2f55595a5cf93218a6b Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 13 Aug 2026 09:30:01 +0100 Subject: [PATCH 1/2] return the fallback when reading a file from a disk that throws `AbstractAdapter::get()` only caught `FileNotFoundException`, which `Storage` disks stopped throwing in Laravel 9. On a disk configured with `throw`, `League\Flysystem\UnableToReadFile` escaped instead, so `Asset::meta()` never received its fallback and missing meta files were never generated. Co-Authored-By: Claude Opus 5 --- src/Filesystem/AbstractAdapter.php | 3 ++- tests/Assets/AssetTest.php | 27 +++++++++++++++++++++++ tests/Filesystem/FlysystemAdapterTest.php | 12 ++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/Filesystem/AbstractAdapter.php b/src/Filesystem/AbstractAdapter.php index 33f40b3c576..4db3fbd6466 100644 --- a/src/Filesystem/AbstractAdapter.php +++ b/src/Filesystem/AbstractAdapter.php @@ -3,6 +3,7 @@ namespace Statamic\Filesystem; use Illuminate\Contracts\Filesystem\FileNotFoundException; +use League\Flysystem\UnableToReadFile; use Statamic\Support\FileCollection; use Statamic\Support\Str; @@ -12,7 +13,7 @@ public function get($path, $fallback = null) { try { return $this->filesystem->get($this->normalizePath($path)) ?: $fallback; - } catch (FileNotFoundException $e) { + } catch (FileNotFoundException|UnableToReadFile $e) { return $fallback; } } diff --git a/tests/Assets/AssetTest.php b/tests/Assets/AssetTest.php index 2ca77788625..90e2d545ebe 100644 --- a/tests/Assets/AssetTest.php +++ b/tests/Assets/AssetTest.php @@ -838,6 +838,33 @@ public function it_generates_meta_on_demand_if_it_doesnt_exist() $this->assertEquals($metaWithData, Cache::get($asset->metaCacheKey())); } + #[Test] + public function it_generates_meta_on_demand_if_it_doesnt_exist_on_a_disk_that_throws_exceptions() + { + Storage::fake('test', ['throw' => true]); + + $file = UploadedFile::fake()->image('image.jpg', 30, 60); // creates a 723 byte image + Storage::disk('test')->putFileAs('foo', $file, 'image.jpg'); + $realFilePath = Storage::disk('test')->path('foo/image.jpg'); + touch($realFilePath, $timestamp = Carbon::parse('2021-02-22 09:41:42')->timestamp); + + $container = Facades\AssetContainer::make('test')->disk('test'); + $asset = (new Asset)->container($container)->path('foo/image.jpg'); + + $meta = [ + 'data' => [], + 'size' => 723, + 'last_modified' => $timestamp, + 'width' => 30, + 'height' => 60, + 'mime_type' => 'image/jpeg', + 'duration' => null, + ]; + + $this->assertEquals($meta, $asset->meta()); + $this->assertEquals($meta, YAML::parse(Storage::disk('test')->get('foo/.meta/image.jpg.yaml'))); + } + #[Test] public function it_generates_meta_on_demand_if_a_required_value_is_missing() { diff --git a/tests/Filesystem/FlysystemAdapterTest.php b/tests/Filesystem/FlysystemAdapterTest.php index b3c2441c2b6..b7f45a04687 100644 --- a/tests/Filesystem/FlysystemAdapterTest.php +++ b/tests/Filesystem/FlysystemAdapterTest.php @@ -41,6 +41,18 @@ protected function makeAdapter() return new FlysystemAdapter($this->filesystem); } + #[Test] + public function gets_fallback_if_file_doesnt_exist_on_a_disk_that_throws_exceptions() + { + $adapter = new FlysystemAdapter(Storage::build([ + 'driver' => 'local', + 'root' => $this->tempDir, + 'throw' => true, + ])); + + $this->assertEquals('Hello World', $adapter->get('filename.txt', 'Hello World')); + } + #[Test] public function it_normalizes_relative_paths() { From 127ca874ce6ebf4dc36b3269254abad96e7da987 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 13 Aug 2026 09:41:32 +0100 Subject: [PATCH 2/2] add a regression test for uploading while the `last_modified` index is in use Covers the other route to the same missing meta read: the indexer calls `lastModified()` during `$store->save()`, before `AssetRepository::save()` writes the meta file. Co-Authored-By: Claude Opus 5 --- tests/Assets/AssetTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/Assets/AssetTest.php b/tests/Assets/AssetTest.php index 90e2d545ebe..a8a520edf2b 100644 --- a/tests/Assets/AssetTest.php +++ b/tests/Assets/AssetTest.php @@ -865,6 +865,23 @@ public function it_generates_meta_on_demand_if_it_doesnt_exist_on_a_disk_that_th $this->assertEquals($meta, YAML::parse(Storage::disk('test')->get('foo/.meta/image.jpg.yaml'))); } + #[Test] + public function it_uploads_to_a_disk_that_throws_exceptions_while_the_last_modified_index_is_in_use() + { + Storage::fake('test', ['throw' => true]); + + $container = Facades\AssetContainer::make('test')->disk('test')->save(); + + Facades\Stache::store('assets::test')->cacheIndexUsage('last_modified'); + + $asset = $container->makeAsset('image.jpg'); + + $asset->upload(UploadedFile::fake()->image('image.jpg', 30, 60)); + + $this->assertTrue(Storage::disk('test')->exists('.meta/image.jpg.yaml')); + $this->assertNotNull($asset->lastModified()); + } + #[Test] public function it_generates_meta_on_demand_if_a_required_value_is_missing() {