laravel/framework · error · LogicException

%s::%s must return a relationship instance, but "null" was r

Error message

%s::%s must return a relationship instance, but "null" was returned. Was the "return" keyword used?

What it means

getRelationshipFromMethod() calls the relation method and checks the result. If it returns exactly null, the most common cause is a missing 'return' keyword inside the method body, so the framework's hint message points at that exact mistake.

Source

Thrown at src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php:641

        throw new LazyLoadingViolationException($this, $key);
    }

    /**
     * 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

View on GitHub (pinned to bd6b5437e6)

Solutions

  1. Add the missing 'return' before the relation builder call.
  2. Run phpstan/pint locally; configure larastan to flag missing returns.
  3. Double-check every method that returns HasMany/BelongsTo etc. ends with 'return $this->...'.

Example fix

// before
public function comments()
{
    $this->hasMany(Comment::class); // no return
}

// after
public function comments()
{
    return $this->hasMany(Comment::class);
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Static analysis is the real guard; at runtime, inspect the method
$ref = new \ReflectionMethod($model, $relation);
if (! str_contains((string) $ref->getBodyText() ?? '', 'return')) {
    throw new \LogicException("{$relation}() may be missing 'return'");
}

Type guard

function relationReturnsInstance(object $model, string $method): bool
{
    $r = $model->{$method}();
    return $r instanceof \Illuminate\Database\Eloquent\Relations\Relation;
}

Try / catch

try {
    return $model->{$relation};
} catch (\LogicException $e) {
    if (str_contains($e->getMessage(), '"null" was returned')) {
        // tell developer to add 'return' in the relation method
    }
    throw $e;
}

Prevention

When it happens

Trigger: Defining a relation method without 'return': public function comments() { $this->hasMany(Comment::class); } and then accessing $model->comments.

Common situations: Copy-paste error omitting 'return'; IDE auto-format that drops the keyword; refactoring a method body and forgetting to keep the return; static analyzers (phpstan) not yet run on the file.

Related errors


AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06). Data as JSON: /data/errors/fe1ac43dbd44f93c.json. Report an issue: GitHub.