Problem
If a database contains an orphaned reference to a User (e.g. last_posted_user_id points to a deleted user) and a RelationAggregator (like countRelation) is configured for the UserResource, navigating to a page that serializes this orphaned relationship crashes the entire forum with a 500 error:
Error: Call to a member function getAttributes() on null in Illuminate\Database\Eloquent\Collection::loadAggregate.
This is a critical robustness issue in Flarum 2.0's EloquentBuffer.
Root cause
This happens due to a combination of Flarum's JSON:API serializer and the EloquentBuffer:
- When JSON:API encounters an un-included relation pointing to an orphaned ID (e.g. a deleted user), it uses a linkage optimization:
$related->newInstance()->forceFill(['id' => $spammerId]). This creates a "mock" model where $model->exists = false.
- This mock model is passed through the resource serialization pipeline, which triggers
UserResource::fields().
- If an extension (like
fof/upload) adds a countRelation aggregator field to UserResource, the mock model is added to EloquentBuffer::$buffer.
- At the end of the request,
EloquentBuffer::load() processes the buffer, calling Laravel's $collection->loadAggregate().
loadAggregate() runs a database query using whereKey([orphanedId]). Because the record doesn't exist, it returns empty.
- Laravel attempts to map the results back to the original models in the collection. When it asks for the attributes of the orphaned ID, it calls
getAttributes() on null, causing a fatal crash.
Reproducer
(This reproduces the issue via fof/anti-spam which leaves orphaned references, but the root cause is in EloquentBuffer)
- Enable
fof/anti-spam and fof/upload.
- As a normal user, create a discussion with a post.
- As an admin, go to that user's profile and use "Mark as Spammer". Select Delete posts and Delete user (but leave Delete discussions unchecked).
(Note: fof/anti-spam bulk-deletes posts, leaving last_posted_user_id on the discussion pointing to the deleted user).
- Navigate to the forum homepage (or any route that lists that discussion) as a Guest.
- The request immediately crashes with
Call to a member function getAttributes() on null.
Suggested fix
Patch Flarum\Api\Resource\EloquentBuffer::load() to filter out models that do not actually exist in the database before passing them to relations or aggregators.
// vendor/flarum/core/src/Api/Resource/EloquentBuffer.php ~ line 117
- $collection = $model->newCollection($models);
+ $collection = $model->newCollection($models)->filter(function ($m) {
+ return $m->exists && $m->getKey() !== null;
+ });
+
+ if ($collection->isEmpty()) {
+ return;
+ }
This ensures loadAggregate only attempts to query and map valid models, making Flarum's API resilient to orphaned database records.
Problem
If a database contains an orphaned reference to a
User(e.g.last_posted_user_idpoints to a deleted user) and aRelationAggregator(likecountRelation) is configured for theUserResource, navigating to a page that serializes this orphaned relationship crashes the entire forum with a 500 error:Error: Call to a member function getAttributes() on nullinIlluminate\Database\Eloquent\Collection::loadAggregate.This is a critical robustness issue in Flarum 2.0's
EloquentBuffer.Root cause
This happens due to a combination of Flarum's JSON:API serializer and the
EloquentBuffer:$related->newInstance()->forceFill(['id' => $spammerId]). This creates a "mock" model where$model->exists = false.UserResource::fields().fof/upload) adds acountRelationaggregator field toUserResource, the mock model is added toEloquentBuffer::$buffer.EloquentBuffer::load()processes the buffer, calling Laravel's$collection->loadAggregate().loadAggregate()runs a database query usingwhereKey([orphanedId]). Because the record doesn't exist, it returns empty.getAttributes()onnull, causing a fatal crash.Reproducer
(This reproduces the issue via
fof/anti-spamwhich leaves orphaned references, but the root cause is inEloquentBuffer)fof/anti-spamandfof/upload.(Note:
fof/anti-spambulk-deletes posts, leavinglast_posted_user_idon the discussion pointing to the deleted user).Call to a member function getAttributes() on null.Suggested fix
Patch
Flarum\Api\Resource\EloquentBuffer::load()to filter out models that do not actually exist in the database before passing them to relations or aggregators.This ensures
loadAggregateonly attempts to query and map valid models, making Flarum's API resilient to orphaned database records.