{"record":{"id":"54d55914279cf846","repo":"yiisoft/yii2","slug":"subquery-for-exists-operator-must-be-a-query-objec","errorCode":null,"errorMessage":"Subquery for EXISTS operator must be a Query object.","messagePattern":"Subquery for EXISTS operator must be a Query object\\.","errorType":"exception","errorClass":"yii\\base\\InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"framework/db/conditions/ExistsCondition.php","lineNumber":51,"sourceCode":"    /**\n     * ExistsCondition constructor.\n     *\n     * @param string $operator the operator to use (e.g. `EXISTS` or `NOT EXISTS`)\n     * @param Query $query the [[Query]] object representing the sub-query.\n     */\n    public function __construct($operator, $query)\n    {\n        $this->operator = $operator;\n        $this->query = $query;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public static function fromArrayDefinition($operator, $operands)\n    {\n        if (!isset($operands[0]) || !$operands[0] instanceof Query) {\n            throw new InvalidArgumentException('Subquery for EXISTS operator must be a Query object.');\n        }\n\n        return new static($operator, $operands[0]);\n    }\n\n    /**\n     * @return string\n     */\n    public function getOperator()\n    {\n        return $this->operator;\n    }\n\n    /**\n     * @return Query\n     */\n    public function getQuery()\n    {","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/yiisoft/yii2/blob/66f00d18a29b520f85e8e8f1e32d1e7e7b556cac/framework/db/conditions/ExistsCondition.php#L33-L69","documentation":"yii\\db\\conditions\\ExistsCondition::fromArrayDefinition() (framework/db/conditions/ExistsCondition.php:51) builds ['exists', X] / ['not exists', X] conditions and requires operands[0] to be an instance of yii\\db\\Query. The EXISTS operators express SQL EXISTS (subquery), so Yii rejects anything that is not a query object — raw SQL string, hash array, or null — with InvalidArgumentException before any SQL is generated.","triggerScenarios":"->where(['exists', 'SELECT id FROM orders']) (raw SQL string); ['not exists', ['id' => 1]] (hash array used as subquery); ['exists', null] when the subquery variable failed to build; passing the result of ->one() instead of the query object itself.","commonSituations":"Converting hand-written SQL with EXISTS subqueries to the query builder; forgetting to wrap the subquery in (new \\yii\\db\\Query())->from(...); passing an ActiveRecord row instead of the ActiveQuery. ActiveQuery extends Query, so active-record subqueries are valid.","solutions":["Wrap the subquery: ->andWhere(['exists', (new \\yii\\db\\Query())->select('1')->from('{{%order}} o')->where('o.customer_id = c.id')])","For negation use ['not exists', $subQuery]","If raw SQL is required, use new \\yii\\db\\Expression('EXISTS (SELECT ...)') with explicit params instead of the array operator"],"exampleFix":"// before\n$query->andWhere(['exists', 'SELECT 1 FROM `order` o WHERE o.customer_id = c.id']); // string -> InvalidArgumentException\n\n// after\n$query->andWhere(['exists', (new \\yii\\db\\Query())\n    ->select('1')\n    ->from('{{%order}} o')\n    ->where('o.customer_id = c.id'),\n]);","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isQueryObject($value): bool\n{\n    return $value instanceof \\yii\\db\\Query;\n}\n\nif (isQueryObject($subQuery)) {\n    $query->andWhere(['not exists', $subQuery]);\n}","tryCatchPattern":"try {\n    $rows = $query->all();\n} catch (\\yii\\base\\InvalidArgumentException $e) {\n    // subquery was not a Query object — rebuild with new Query()\n    \\Yii::error($e->getMessage(), __METHOD__);\n}","preventionTips":["Always build subqueries with new \\yii\\db\\Query() (or an ActiveQuery) — never pass SQL strings into ['exists', ...]","Never feed ->one()/->all() results back as a condition operand","For raw SQL EXISTS use \\yii\\db\\Expression with bound parameters"],"tags":["yii2","database","query-builder","exists","subquery"],"backgroundTag":"malformed-query-condition","analyzedSha":"66f00d18a29b520f85e8e8f1e32d1e7e7b556cac","analyzedAt":"2026-08-17T05:17:23.470Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}