yiisoft/yii2 · error · yii\base\InvalidArgumentException
Subquery for EXISTS operator must be a Query object.
Error message
Subquery for EXISTS operator must be a Query object.
What it means
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.
Source
Thrown at framework/db/conditions/ExistsCondition.php:51
/**
* ExistsCondition constructor.
*
* @param string $operator the operator to use (e.g. `EXISTS` or `NOT EXISTS`)
* @param Query $query the [[Query]] object representing the sub-query.
*/
public function __construct($operator, $query)
{
$this->operator = $operator;
$this->query = $query;
}
/**
* {@inheritdoc}
*/
public static function fromArrayDefinition($operator, $operands)
{
if (!isset($operands[0]) || !$operands[0] instanceof Query) {
throw new InvalidArgumentException('Subquery for EXISTS operator must be a Query object.');
}
return new static($operator, $operands[0]);
}
/**
* @return string
*/
public function getOperator()
{
return $this->operator;
}
/**
* @return Query
*/
public function getQuery()
{View on GitHub (pinned to 66f00d18a2)
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
Example fix
// before
$query->andWhere(['exists', 'SELECT 1 FROM `order` o WHERE o.customer_id = c.id']); // string -> InvalidArgumentException
// after
$query->andWhere(['exists', (new \yii\db\Query())
->select('1')
->from('{{%order}} o')
->where('o.customer_id = c.id'),
]); Defensive patterns
Strategy: type-guard
Type guard
function isQueryObject($value): bool
{
return $value instanceof \yii\db\Query;
}
if (isQueryObject($subQuery)) {
$query->andWhere(['not exists', $subQuery]);
} Try / catch
try {
$rows = $query->all();
} catch (\yii\base\InvalidArgumentException $e) {
// subquery was not a Query object — rebuild with new Query()
\Yii::error($e->getMessage(), __METHOD__);
} Prevention
- 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
When it happens
Trigger: ->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.
Common situations: 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.
Related errors
- yii\db\mssql\conditions\InConditionBuilder::buildSubqueryInC
- Operator '$operator' requires three operands.
- Operator '$operator' requires two operands.
- Operator '$operator' requires two operands.
- Invalid operator '$operator'.
AI-assisted analysis of yiisoft/yii2@66f00d18a2 (2026-08-17).
Data as JSON: /api/errors/54d55914279cf846.
Report an issue: GitHub.