yiisoft/yii2 · error · yii\base\InvalidConfigException

The ArrayExpression class can not be iterated when the value

Error message

The ArrayExpression class can not be iterated when the value is a QueryInterface object

What it means

ArrayExpression wraps either a concrete PHP array or a subquery (QueryInterface) for use as an SQL array literal. Its getIterator() (invoked by foreach) can only walk a real array; when the wrapped value is a QueryInterface instance there is nothing to iterate client-side, so InvalidConfigException is thrown instead of silently iterating the query object.

Source

Thrown at framework/db/ArrayExpression.php:199

    {
        return count($this->value);
    }

    /**
     * Retrieve an external iterator
     *
     * @link https://www.php.net/manual/en/iteratoraggregate.getiterator.php
     * @return Traversable An instance of an object implementing <b>Iterator</b> or
     * <b>Traversable</b>
     * @since 2.0.14.1
     * @throws InvalidConfigException when ArrayExpression contains QueryInterface object
     */
    #[\ReturnTypeWillChange]
    public function getIterator()
    {
        $value = $this->getValue();
        if ($value instanceof QueryInterface) {
            throw new InvalidConfigException('The ArrayExpression class can not be iterated when the value is a QueryInterface object');
        }
        if ($value === null) {
            $value = [];
        }

        return new \ArrayIterator($value);
    }
}

View on GitHub (pinned to 66f00d18a2)

Solutions

  1. Keep the original PHP array for iteration and use ArrayExpression only when composing SQL
  2. Guard before iterating: $v = $expr->getValue(); if (!$v instanceof QueryInterface) { foreach ($v as ...) }
  3. If you actually need the subquery's rows, run it (->column()/->all()) and iterate the result
  4. Type-hint shared helpers to accept only arrays, not Traversable, when expression inputs are possible

Example fix

// before
$expr = new ArrayExpression((new Query())->select('id')->from('tag'));
foreach ($expr as $tagId) { ... } // throws

// after
$tagIds = (new Query())->select('id')->from('tag')->column();
foreach ($tagIds as $tagId) { ... }
// use new ArrayExpression($tagIds) only when embedding in SQL
Defensive patterns

Strategy: type-guard

Type guard

function isIterableArrayExpression(\yii\db\ArrayExpression $expr): bool
{
    $value = $expr->getValue();
    return $value === null || !$value instanceof \yii\db\QueryInterface;
}

Try / catch

try {
    foreach ($expr as $item) { /* ... */ }
} catch (yii\base\InvalidConfigException $e) {
    if (strpos($e->getMessage(), 'ArrayExpression') !== false) {
        $items = (new Query())->... ->column(); // materialize the subquery instead
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: foreach ($arrayExpression as $v) after constructing it with (new Query())->select('id')->from('t') as the value; passing the expression into generic helpers that iterate their input (ArrayHelper, implode, json_encode of traversables); debugging by looping over an expression built for SQL.

Common situations: Code that accepts array|Expression union types and hits the subquery variant; developers inspecting expression objects during query debugging; reusing a value that is sometimes an array and sometimes a subquery.

Related errors


AI-assisted analysis of yiisoft/yii2@66f00d18a2 (2026-08-17). Data as JSON: /api/errors/a30d39156939855d. Report an issue: GitHub.