Skip to content

Term::fromModel() fails with Call to a member function sites() on null due to Blink::once() race condition #528

@BartWaardenburg

Description

@BartWaardenburg

Bug description

When using the Eloquent driver for both taxonomies and terms, API requests to /api/taxonomies/{handle}/terms fail with:

Call to a member function sites() on null

The error occurs in src/Taxonomies/Term.php:152 in the defaultLocale() method.

How to reproduce

  1. Configure Statamic to use Eloquent driver for:

    • taxonomies
    • terms
    • sites
  2. Seed taxonomy terms to the database using dataForLocale('default', [...])

  3. Make an API request to /api/taxonomies/organization_types/terms

  4. The request fails with the error above

Root cause analysis

The issue is in Term::fromModel() in the eloquent-driver (vendor/statamic/eloquent-driver/src/Taxonomies/Term.php:26):

$term = (new static)
    ->slug($model->slug)
    ->taxonomy($model->taxonomy)  // Sets taxonomy handle as string
    ->model($model)
    ->blueprint($model->data['blueprint'] ?? null);

collect($data['localizations'] ?? [])
    ->except($term->defaultLocale())  // <-- Calls taxonomy()->sites()->first()

When defaultLocale() is called, it invokes $this->taxonomy() (the getter), which does:

->getter(function ($taxonomy) {
    return $taxonomy ? Blink::once("taxonomy-{$taxonomy}", function () use ($taxonomy) {
        return Taxonomy::findByHandle($taxonomy);
    }) : null;
})

The problem: Due to timing issues in the request lifecycle, Blink::once() can cache null even when the taxonomy exists in the database. Once null is cached, subsequent calls return null, causing ->sites() to fail.

Evidence from debugging

  • Warming the Blink cache with Blink::put("taxonomy-{$handle}", $taxonomy) in middleware before the controller runs does NOT fix the issue
  • The cache shows has('taxonomy-organization_types') = true both before AND after the controller fails with 500
  • Taxonomy::all() and Taxonomy::findByHandle() work correctly in isolation (e.g., in tinker)
  • The issue only manifests during web API requests with the Eloquent driver

Workaround

We implemented a middleware that intercepts /api/taxonomies/{handle}/terms requests and queries the database directly, bypassing Term::fromModel() entirely:

$terms = DB::table('taxonomy_terms')
    ->where('taxonomy', $taxonomyHandle)
    ->where('site', 'default')
    ->get();

Versions

Software Version
Statamic 5.x (latest)
Laravel 11.x
Eloquent Driver latest
PHP 8.3

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions