laravel/framework · error · LogicException
:: must return a relationship instance.
Error message
%s::%s must return a relationship instance.
What it means
Thrown by getRelationshipFromMethod() when a method Eloquent treats as a relation returns a value that is neither a Relation instance nor null. Unlike the null case, the method returned something (a string, array, builder, model, etc.), so the generic message is used. Eloquent requires relation methods to return an object implementing Relation.
Solutions
- Ensure the method returns the Relation object itself without a terminal execution call (no ->get() / ->first() inside).
- Move query scoping into a scope method or apply it at call sites, not in the relation definition.
- If you need pre-filtered results, return the Relation and let callers add ->where().
Example fix
// before
public function publishedPosts()
{
return $this->hasMany(Post::class)->where('published', true)->get();
}
// after
public function publishedPosts()
{
return $this->hasMany(Post::class)->where('published', true);
} Defensive patterns
Strategy: type-guard
Validate before calling
$result = $model->{$method}();
if (! $result instanceof \Illuminate\Database\Eloquent\Relations\Relation) {
throw new \LogicException(static::class . "::{$method} returned " . get_debug_type($result));
}
Type guard
function returnsRelationInstance(string $class, string $method): bool
{
if (! method_exists($class, $method)) {
return false;
}
$result = (new $class)->{$method}();
return $result instanceof \Illuminate\Database\Eloquent\Relations\Relation;
} Prevention
- Never chain terminal executors (->get(), ->first(), ->pluck()) inside the relation definition.
- Keep relation methods minimal: return the Relation object with optional query scoping only.
- Statically analyze relation return types (PHPStan level with larastan).
When it happens
Trigger: A relation method that returns a query builder ($this->hasMany(...)->where(...)) where the chain ends on a non-Relation (e.g. returns a Builder or a Model from ->first()), or returns a plain array/scalar. Accessing the relation runs getRelationshipFromMethod() which fails the instanceof Relation check.
Common situations: Chaining a terminal method (->first(), ->get(), ->pluck()) onto the relation definition so it returns results instead of a Relation; returning a custom object that does not extend Relation; a method named like a relation that returns cached data.
Related errors
- :: must return a relationship instance, but "null" was…
- Collection given to whereAttachedTo method may not be empty.
- Collection given to whereBelongsTo method may not be empty.
- Collection given to whereMorphedTo method may not be empty.
- Collection given to whereNotMorphedTo method may not be…
AI-assisted analysis of laravel/framework@e0f6eb3518 (2026-08-11).
Data as JSON: /api/errors/d8d560faab695df9.
Report an issue: GitHub.
Appendix: 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 e0f6eb3518)