laravel/framework · error · LogicException
%s::%s must return a relationship instance.
Error message
%s::%s must return a relationship instance.
What it means
Same method as 148, but the relation returned a non-null value that is not a Relation instance (e.g. a Builder, Collection, Model, or scalar). Eloquent can only hydrate from a real Relation, so it refuses.
Source
Thrown at src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php:646
* Get a relationship value from a method.
*
* @param string $method
* @return mixed
*
* @throws \LogicException
*/
protected function getRelationshipFromMethod($method)
{
$relation = Relation::withConstraintsForNestedRelation(fn () => $this->$method());
if (! $relation instanceof Relation) {
if (is_null($relation)) {
throw new LogicException(sprintf(
'%s::%s must return a relationship instance, but "null" was returned. Was the "return" keyword used?', static::class, $method
));
}
throw new LogicException(sprintf(
'%s::%s must return a relationship instance.', static::class, $method
));
}
return tap($relation->getResults(), function ($results) use ($method) {
$this->setRelation($method, $results);
});
}
/**
* Determine if a get mutator exists for an attribute.
*
* @param string $key
* @return bool
*/
public function hasGetMutator($key)
{
return method_exists($this, 'get'.Str::studly($key).'Attribute');View on GitHub (pinned to bd6b5437e6)
Solutions
- Return only the Relation instance: return $this->hasMany(Comment::class); move ->where() into a scope or caller.
- If you need constraints, define them via a closure passed to hasMany/hasOne: $this->hasMany(Comment::class)->where('active', 1).
- For computed data, expose a separate accessor method, not a relation method.
Example fix
// before
public function activeComments()
{
return $this->hasMany(Comment::class)->where('active', 1)->get();
}
// after
public function activeComments()
{
return $this->hasMany(Comment::class)->where('active', 1);
} Defensive patterns
Strategy: type-guard
Validate before calling
$r = $model->{$relation}();
if (! $r instanceof \Illuminate\Database\Eloquent\Relations\Relation) {
throw new \LogicException(get_class($model) . '::' . $relation . ' returned ' . get_debug_type($r));
} Type guard
function isRelationMethod(object $model, string $method): bool
{
return $model->{$method}() instanceof \Illuminate\Database\Eloquent\Relations\Relation;
} Try / catch
try {
return $model->{$relation};
} catch (\LogicException $e) {
if (str_contains($e->getMessage(), 'must return a relationship instance')) {
report(get_class($model) . '::' . $relation . ' returns a non-Relation');
}
throw $e;
} Prevention
- Do not chain ->get()/->first() inside relation methods; chain query constraints only.
- Use Larastan to flag methods declared as Relation that return other types.
- Cover each relation with a test asserting the return type.
When it happens
Trigger: A relation method that returns $this->hasMany(...)->where(...) when chained into a Builder, returns ->get() (a Collection), returns ->first() (a Model), or returns an arbitrary value; then accessing the relation triggers the error.
Common situations: Chaining ->get()/->first() inside the relation method; returning a query-scope result; refactoring that returned the wrong builder type; conditional return paths that return mixed types.
Related errors
- %s::%s must return a relationship instance, but "null" was r
- The provided class must extend [{Collection::class}].
- The provided class must extend [{Collection::class}].
- Unable to create query for collection with mixed types.
- Attempted to lazy load [{$relation}] on model [{$class}] but
AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06).
Data as JSON: /data/errors/d8d560faab695df9.json.
Report an issue: GitHub.