{"record":{"id":"fba3fddb09f1eccf","repo":"laravel/framework","slug":"a-subquery-must-be-a-query-builder-instance-a-clo","errorCode":null,"errorMessage":"A subquery must be a query builder instance, a Closure, or a string.","messagePattern":"A subquery must be a query builder instance, a Closure, or a string\\.","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Illuminate/Database/Query/Builder.php","lineNumber":436,"sourceCode":"\n    /**\n     * Parse the subquery into SQL and bindings.\n     *\n     * @param  mixed  $query\n     * @return array\n     *\n     * @throws \\InvalidArgumentException\n     */\n    protected function parseSub($query)\n    {\n        if ($query instanceof self || $query instanceof EloquentBuilder || $query instanceof Relation) {\n            $query = $this->prependDatabaseNameIfCrossDatabaseQuery($query);\n\n            return [$query->toSql(), $query->getBindings()];\n        } elseif (is_string($query)) {\n            return [$query, []];\n        } else {\n            throw new InvalidArgumentException(\n                'A subquery must be a query builder instance, a Closure, or a string.'\n            );\n        }\n    }\n\n    /**\n     * Prepend the database name if the given query is on another database.\n     *\n     * @param  mixed  $query\n     * @return mixed\n     */\n    protected function prependDatabaseNameIfCrossDatabaseQuery($query)\n    {\n        if ($query->getConnection()->getDatabaseName() !==\n            $this->getConnection()->getDatabaseName()) {\n            $databaseName = $query->getConnection()->getDatabaseName();\n\n            if (! str_starts_with($query->from, $databaseName) && ! str_contains($query->from, '.')) {","sourceCodeStart":418,"sourceCodeEnd":454,"githubUrl":"https://github.com/laravel/framework/blob/e0f6eb3518ac29fbbca8529e97d0df7fc9f24481/src/Illuminate/Database/Query/Builder.php#L418-L454","documentation":"Many query builder methods accept a subquery (whereIn, whereNotIn, whereExists, joinSub, fromSub, etc.). parseSub() normalizes that argument and accepts only three shapes: a Query\\Builder, Eloquent\\Builder, or Relation; a raw SQL string; or — at the calling sites — a Closure that resolves to a builder. Anything else fails fast with InvalidArgumentException so an invalid subquery never reaches the grammar layer.","triggerScenarios":"Passing an array, Collection, Model instance, integer, or null where a subquery is expected. Examples: whereIn('id', $model) where $model is a Model (not a builder); joinSub([1,2], 'sub', ...) with an array; whereExists($scalar).","commonSituations":"Forgetting to call ->getQuery() or ->toBase() on an Eloquent scope/relation; passing a Collection instead of a query builder; passing a relation proxy where a subselect is needed; mis-chaining so a scalar ends up in the subquery slot.","solutions":["Wrap your logic in a Closure that receives the subquery builder: ->whereIn('id', function ($q) { $q->select(...); }).","Pass an explicit Query\\Builder or Eloquent\\Builder — call ->toBase() / ->getQuery() on a relation or scoped builder to get one.","If the subquery is static SQL, pass it as a raw string (DB::raw() not required here; a plain string is accepted)."],"exampleFix":"// before\n->whereIn('user_id', User::where('active', true))\n\n// after\n->whereIn('user_id', User::where('active', true)->pluck('id'))\n// or a real subquery:\n->whereIn('user_id', function ($q) {\n    $q->select('id')->from('users')->where('active', true);\n})","handlingStrategy":"validation","validationCode":"use Illuminate\\Database\\Query\\Builder as QueryBuilder;\nuse Illuminate\\Database\\Eloquent\\Builder as EloquentBuilder;\nuse Illuminate\\Database\\Eloquent\\Relations\\Relation;\n\nfunction isSubqueryable(mixed $q): bool\n{\n    return $q instanceof QueryBuilder\n        || $q instanceof EloquentBuilder\n        || $q instanceof Relation\n        || is_string($q)\n        || $q instanceof Closure;\n}\n\nif (! isSubqueryable($arg)) {\n    throw new InvalidArgumentException('Argument must be a builder, Relation, Closure, or SQL string.');\n}","typeGuard":"function isSubqueryable(mixed $q): bool\n{\n    return $q instanceof \\Illuminate\\Database\\Query\\Builder\n        || $q instanceof \\Illuminate\\Database\\Eloquent\\Builder\n        || $q instanceof \\Illuminate\\Database\\Eloquent\\Relations\\Relation\n        || is_string($q)\n        || $q instanceof \\Closure;\n}","tryCatchPattern":null,"preventionTips":["Prefer Closure-based subqueries — they read clearly and the builder is constructed for you.","Call ->toBase() / ->getQuery() on relations and scopes when passing them as subqueries.","Never pass a Collection or Model instance where a subquery is expected."],"tags":["query-builder","subquery","database","types"],"backgroundTag":null,"analyzedSha":"e0f6eb3518ac29fbbca8529e97d0df7fc9f24481","analyzedAt":"2026-08-11T20:52:37.562Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}