yiisoft/yii2 · error · InvalidConfigException

Primary key of '{$class}' can not be empty.

Error message

Primary key of '{$class}' can not be empty.

What it means

After a JOIN query, ActiveQuery::populate() calls removeDuplicatedModels() to drop rows duplicated by the join, building a uniqueness hash from the model's primary key values. When $class::primaryKey() returns an empty array (the underlying table declares no primary key), the hash key cannot be computed and InvalidConfigException is thrown. The dedup path only runs when the query has joins and indexBy is null, so plain queries on the same model work fine.

Source

Thrown at framework/db/ActiveQuery.php:283

            // composite primary key
            foreach ($models as $i => $model) {
                $key = [];
                foreach ($pks as $pk) {
                    if (!isset($model[$pk])) {
                        // do not continue if the primary key is not part of the result set
                        break 2;
                    }
                    $key[] = $model[$pk];
                }
                $key = serialize($key);
                if (isset($hash[$key])) {
                    unset($models[$i]);
                } else {
                    $hash[$key] = true;
                }
            }
        } elseif (empty($pks)) {
            throw new InvalidConfigException("Primary key of '{$class}' can not be empty.");
        } else {
            // single column primary key
            $pk = reset($pks);
            foreach ($models as $i => $model) {
                if (!isset($model[$pk])) {
                    // do not continue if the primary key is not part of the result set
                    break;
                }
                $key = $model[$pk];
                if (isset($hash[$key])) {
                    unset($models[$i]);
                } elseif ($key !== null) {
                    $hash[$key] = true;
                }
            }
        }

        return array_values($models);

View on GitHub (pinned to 66f00d18a2)

Solutions

  1. Add a PRIMARY KEY constraint to the underlying table (preferred)
  2. Override primaryKey() in the AR class to return the column(s) that uniquely identify a row, e.g. public static function primaryKey() { return ['id']; }
  3. Set ->indexBy('unique_column') on the query to bypass the dedup logic entirely
  4. Remove the join if it is not needed, or deduplicate at SQL level (SELECT DISTINCT / GROUP BY)

Example fix

// before
class SalesView extends \yii\db\ActiveRecord
{
    public static function tableName() { return 'vw_sales'; }
}
$rows = SalesView::find()->joinWith('customer')->all(); // throws

// after
class SalesView extends \yii\db\ActiveRecord
{
    public static function tableName() { return 'vw_sales'; }
    public static function primaryKey() { return ['sales_id']; }
}
$rows = SalesView::find()->joinWith('customer')->all();
Defensive patterns

Strategy: validation

Validate before calling

if (!empty($this->join) && $query->indexBy === null && empty($modelClass::primaryKey())) {
    throw new InvalidConfigException("{$modelClass} has no primary key; JOIN deduplication is impossible.");
}
$rows = $query->all();

Try / catch

try {
    $rows = Model::find()->joinWith('rel')->all();
} catch (yii\base\InvalidConfigException $e) {
    if (strpos($e->getMessage(), 'Primary key') === 0) {
        // fall back to a keyless query without dedup
        $rows = Model::find()->indexBy('id')->joinWith('rel')->all();
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Model::find()->joinWith('relation')->all(), or ->innerJoin(...)->all(), where the model's table or view has no primary key and indexBy is not set; the same via ->leftJoin() with ->with() eager loading.

Common situations: AR classes mapped to MySQL/MariaDB views (which cannot declare primary keys); legacy tables created without a PRIMARY KEY constraint; stale schema cache after the key was added; queries that worked until a join was introduced.

Related errors


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