Skip to content

Glide tag 500s the whole page when an asset URL resolves to null (isVideo() on null) #15359

Description

@jurnskie

Bug description

When the {{ glide }} tag is given an asset URL (or path) that Asset::find() cannot resolve, but the file itself is readable on the container disk, the tag does not skip the image. It passes null into ImageGenerator::generateByAsset(), which immediately calls $asset->isVideo():

// src/Imaging/ImageGenerator.php:156-164
public function generateByAsset($asset, array $params)
{
    if (ThumbnailExtractor::available() && $asset->isVideo()) {
        // ...
    }

    if ($asset->isVideo()) {
        return '';
    }

That throws Error: Call to a member function isVideo() on null. Because it is an \Error and not an \Exception, it escapes the tag's own error handling in Glide::generate():

// src/Tags/Glide.php:134-152
$items = $items->map(function ($item) {
    try {
        $data = ['url' => $this->generateGlideUrl($item)];

        if ($this->isValidExtension($item)) {
            $path = $this->generateImage($item);   // -> generateByAsset(Asset::find($item), $params)
            // ...
        }
        // ...
    } catch (\Exception $e) {
        Log::error($e->getMessage());
    }
})->filter()->all();

So one unresolvable asset takes down the entire page with a 500 instead of rendering without that image. Every other failure mode in this closure (unreadable file, invalid manipulation, etc.) is an \Exception and degrades gracefully; only the "repository returned null" case is fatal.

Expected: an asset that cannot be resolved is logged and skipped, like every other error inside the tag.
Actual: the whole request fails with Call to a member function isVideo() on null.

How to reproduce

Any situation where the file exists on the container disk but the asset repository returns null for its URL. The concrete case we hit in production:

  1. Statamic 6.31 with statamic/eloquent-driver 5.11 for assets.
  2. Upload an asset with a space, parenthesis or @ in its filename, e.g. logo (1).png. Since [6.x] Encode asset URLs #15146 its URL is /assets/logo%20%281%29.png.
  3. The Eloquent driver's findByUrl() does not decode the path (findByUrl() does not decode percent-encoded paths, returning null for filenames with spaces, parentheses or @ eloquent-driver#609, fix in Decode percent-encoded paths in AssetRepository::findByUrl() eloquent-driver#610), so Asset::find('/assets/logo%20%281%29.png') returns null.
  4. Render {{ glide src="/assets/logo%20%281%29.png" width="100" }}{{ url }}{{ /glide }} — or simply any page whose og:image (SEO Pro) or content references that asset.
  5. Result: 500, Call to a member function isVideo() on null, vendor/statamic/cms/src/Imaging/ImageGenerator.php:158.

The driver bug is being fixed upstream, but the tag's behaviour is the part that turned a missing image into a site-wide outage (75 occurrences across all tenants of a multi-site install within a day). The same fatal should be reachable without the Eloquent driver: #15146 notes that an old, unencoded URL to a file whose name contains a literal % no longer resolves via findByUrl(), while the file itself is still readable at that path. (I could not verify that variant in my environment, so treat it as a plausible second trigger rather than a confirmed one.)

Minimal call that shows the fatal directly:

app(\Statamic\Imaging\ImageGenerator::class)->generateByAsset(null, []);
// Error: Call to a member function isVideo() on null @ ImageGenerator.php:158

Logs

Error: Call to a member function isVideo() on null
#0 vendor/statamic/cms/src/Imaging/ImageGenerator.php:158 Statamic\Imaging\ImageGenerator::generateByAsset()
#1 vendor/statamic/cms/src/Tags/Glide.php:180 Statamic\Tags\Glide::generateImage()
#2 vendor/statamic/cms/src/Tags/Glide.php:139 Statamic\Tags\Glide::{closure}()
#3 vendor/laravel/framework/src/Illuminate/Collections/Arr.php:858 Illuminate\Support\Arr::map()
#4 vendor/laravel/framework/src/Illuminate/Collections/Collection.php:831 Illuminate\Support\Collection::map()
#5 vendor/statamic/cms/src/Tags/Glide.php:134 Statamic\Tags\Glide::generate()
#6 vendor/statamic/cms/src/Tags/Glide.php:54 Statamic\Tags\Glide::index()
#7 vendor/statamic/cms/src/View/Antlers/Language/Runtime/NodeProcessor.php:1658 ...

Environment

Statamic 6.31.0 (Pro), Laravel 13.26.1, PHP 8.4.17, statamic/eloquent-driver 5.11.0, statamic/seo-pro 7.13.3, Antlers runtime parser.

Installation

Existing Laravel app (multi-tenant, one codebase serving several sites).

Additional details

Suggested fix, either or both:

  • In Glide::generateImage(), return early (or throw an \Exception) when Asset::find($item) is null, so the tag's existing catch (\Exception) handles it and the image is skipped with a log line.
  • In ImageGenerator::generateByAsset(), guard against a null asset with a descriptive exception instead of dereferencing it.

Happy to open a PR for either if you have a preference.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions