yiisoft/yii2 · error · InvalidConfigException

"{class}::$query" should be an instance of "yii\db\QueryInte

Error message

"{class}::$query" should be an instance of "yii\db\QueryInterface".

What it means

Thrown by yii\caching\DbQueryDependency::generateDependencyData() when the query property is not an instance of yii\db\QueryInterface — which includes the null case of never setting it. This dependency tracks changes by executing a Query object and hashing its results, so a raw SQL string, an array, a class name, or null cannot be used; ActiveQuery instances qualify because they implement QueryInterface.

Source

Thrown at framework/caching/DbQueryDependency.php:79


    /**
     * Generates the data needed to determine if dependency is changed.
     *
     * This method returns the query result.
     * @param CacheInterface $cache the cache component that is currently evaluating this dependency
     * @return mixed the data needed to determine if dependency has been changed.
     * @throws InvalidConfigException on invalid configuration.
     */
    protected function generateDependencyData($cache)
    {
        $db = $this->db;
        if ($db !== null) {
            $db = Instance::ensure($db);
        }

        if (!$this->query instanceof QueryInterface) {
            throw new InvalidConfigException('"' . get_class($this) . '::$query" should be an instance of "yii\db\QueryInterface".');
        }

        if (!empty($db->enableQueryCache)) {
            // temporarily disable and re-enable query caching
            $originEnableQueryCache = $db->enableQueryCache;
            $db->enableQueryCache = false;
            $result = $this->executeQuery($this->query, $db);
            $db->enableQueryCache = $originEnableQueryCache;
        } else {
            $result = $this->executeQuery($this->query, $db);
        }

        return $result;
    }

    /**
     * Executes the query according to [[method]] specification.
     * @param QueryInterface $query query to be executed.

View on GitHub (pinned to 66f00d18a2)

Solutions

  1. Set query to a Query instance: 'query' => (new Query())->from('post')->orderBy('updated_at DESC')->limit(1) (values are hashed, keep them deterministic and small).
  2. Remember ActiveQuery works too: 'query' => Post::find()->select(['updated_at'])->orderBy(['id' => SORT_DESC])->limit(1).
  3. Never use raw SQL here — that is DbDependency's sql property.
  4. Add an assertion right after constructing the dependency if it comes from dynamic config.

Example fix

// before
$dependency = new DbQueryDependency([
    'query' => 'SELECT MAX(updated_at) FROM post', // string, not QueryInterface -> throws
]);

// after
$dependency = new DbQueryDependency([
    'query' => (new Query())
        ->select(['MAX(updated_at)'])
        ->from('post'),
]);
Defensive patterns

Strategy: validation

Validate before calling

if (!$dependency->query instanceof \yii\db\QueryInterface) {
    $dependency->query = (new \yii\db\Query())->from('post')->select(['MAX(updated_at)']);
}
Yii::$app->cache->set($key, $data, 0, $dependency);

Type guard

function isCacheableQuery($query): bool
{
    return $query instanceof \yii\db\QueryInterface;
}

Prevention

When it happens

Trigger: new DbQueryDependency(['query' => 'SELECT MAX(id) FROM post']) — a string instead of a Query; omitting query entirely; passing an array config like ['select' => ...] intended for a Query that was never instantiated; passing the AR class name or a query built for another component type (e.g. an Elasticsearch query object that does not implement the interface).

Common situations: Confusing DbDependency (sql string) with DbQueryDependency (Query object) and passing sql-style values; refactoring a dependency builder where the query construction was skipped; swapping the backing store while keeping the old dependency shape; IDE auto-completing 'sql' out of habit.

Related errors


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