{"record":{"id":"d8d560faab695df9","repo":"laravel/framework","slug":"s-s-must-return-a-relationship-instance","errorCode":null,"errorMessage":"%s::%s must return a relationship instance.","messagePattern":"(.+?)::(.+?) must return a relationship instance\\.","errorType":"exception","errorClass":"LogicException","httpStatus":null,"severity":"error","filePath":"src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php","lineNumber":646,"sourceCode":"     * Get a relationship value from a method.\n     *\n     * @param  string  $method\n     * @return mixed\n     *\n     * @throws \\LogicException\n     */\n    protected function getRelationshipFromMethod($method)\n    {\n        $relation = Relation::withConstraintsForNestedRelation(fn () => $this->$method());\n\n        if (! $relation instanceof Relation) {\n            if (is_null($relation)) {\n                throw new LogicException(sprintf(\n                    '%s::%s must return a relationship instance, but \"null\" was returned. Was the \"return\" keyword used?', static::class, $method\n                ));\n            }\n\n            throw new LogicException(sprintf(\n                '%s::%s must return a relationship instance.', static::class, $method\n            ));\n        }\n\n        return tap($relation->getResults(), function ($results) use ($method) {\n            $this->setRelation($method, $results);\n        });\n    }\n\n    /**\n     * Determine if a get mutator exists for an attribute.\n     *\n     * @param  string  $key\n     * @return bool\n     */\n    public function hasGetMutator($key)\n    {\n        return method_exists($this, 'get'.Str::studly($key).'Attribute');","sourceCodeStart":628,"sourceCodeEnd":664,"githubUrl":"https://github.com/laravel/framework/blob/e0f6eb3518ac29fbbca8529e97d0df7fc9f24481/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L628-L664","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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()."],"exampleFix":"// before\npublic function publishedPosts()\n{\n    return $this->hasMany(Post::class)->where('published', true)->get();\n}\n\n// after\npublic function publishedPosts()\n{\n    return $this->hasMany(Post::class)->where('published', true);\n}","handlingStrategy":"type-guard","validationCode":"$result = $model->{$method}();\nif (! $result instanceof \\Illuminate\\Database\\Eloquent\\Relations\\Relation) {\n    throw new \\LogicException(static::class . \"::{$method} returned \" . get_debug_type($result));\n}\n","typeGuard":"function returnsRelationInstance(string $class, string $method): bool\n{\n    if (! method_exists($class, $method)) {\n        return false;\n    }\n    $result = (new $class)->{$method}();\n    return $result instanceof \\Illuminate\\Database\\Eloquent\\Relations\\Relation;\n}","tryCatchPattern":null,"preventionTips":["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)."],"tags":["eloquent","relations","developer-error","query-builder"],"backgroundTag":null,"analyzedSha":"e0f6eb3518ac29fbbca8529e97d0df7fc9f24481","analyzedAt":"2026-08-11T20:52:37.562Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}