yiisoft/yii2 · error · yii\base\InvalidConfigException
"{class}" must have a primary key.
Error message
"{class}" must have a primary key. What it means
BaseActiveRecord::findByCondition() implements findOne()/findAll() for all ActiveRecord flavors built on the base class. When the condition is a scalar or a non-associative array (and not an ExpressionInterface), it is interpreted as primary key value(s) and wrapped as [pk => value]; with static::primaryKey() empty, the wrap is impossible and InvalidConfigException is thrown.
Source
Thrown at framework/db/BaseActiveRecord.php:142
* Finds ActiveRecord instance(s) by the given condition.
* This method is internally called by [[findOne()]] and [[findAll()]].
* @param mixed $condition please refer to [[findOne()]] for the explanation of this parameter
* @return ActiveQueryInterface the newly created [[ActiveQueryInterface|ActiveQuery]] instance.
* @throws InvalidConfigException if there is no primary key defined
* @internal
*/
protected static function findByCondition($condition)
{
$query = static::find();
if (!ArrayHelper::isAssociative($condition) && !$condition instanceof ExpressionInterface) {
// query by primary key
$primaryKey = static::primaryKey();
if (isset($primaryKey[0])) {
// if condition is scalar, search for a single primary key, if it is array, search for multiple primary key values
$condition = [$primaryKey[0] => is_array($condition) ? array_values($condition) : $condition];
} else {
throw new InvalidConfigException('"' . get_called_class() . '" must have a primary key.');
}
}
return $query->andWhere($condition);
}
/**
* Updates the whole table using the provided attribute values and conditions.
*
* For example, to change the status to be 1 for all customers whose status is 2:
*
* ```
* Customer::updateAll(['status' => 1], 'status = 2');
* ```
*
* @param array $attributes attribute values (name-value pairs) to be saved into the table
* @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.
* Please refer to [[Query::where()]] on how to specify this parameter.View on GitHub (pinned to 66f00d18a2)
Solutions
- Define a primary key in the underlying storage and let primaryKey() pick it up
- Override primaryKey() to return the identifying column name(s)
- Call with an associative condition instead: Model::findOne(['uuid' => $uuid])
- Bypass findOne/findAll: Model::find()->andWhere(['uuid' => $uuid])->one()
Example fix
// before $session = SessionModel::findOne($id); // throws: no PK defined // after $session = SessionModel::find()->where(['session_id' => $id])->one();
Defensive patterns
Strategy: validation
Validate before calling
if (!ArrayHelper::isAssociative($condition) && empty($modelClass::primaryKey())) {
// scalar condition on a PK-less model: switch to explicit column lookup
$condition = [$fallbackColumn => $condition];
}
$model = $modelClass::findOne($condition); Try / catch
try {
$model = SessionModel::findOne($id);
} catch (yii\base\InvalidConfigException $e) {
if (strpos($e->getMessage(), 'must have a primary key') !== false) {
$model = SessionModel::find()->where(['session_id' => $id])->one();
} else {
throw $e;
}
} Prevention
- Define primaryKey() in every custom BaseActiveRecord subclass as a skeleton requirement
- Use associative conditions in generic code paths shared across AR flavors
- Assert non-empty primaryKey() in model unit tests
When it happens
Trigger: Model::findOne(5) or Model::findAll([1, 2]) on any AR subclass whose backing table/schema declares no primary key and whose primaryKey() is not overridden — including custom AR classes extending BaseActiveRecord directly.
Common situations: Keyless tables or views modeled as AR; custom AR implementations (file/API-backed) that never defined primaryKey(); schema metadata unavailable for the storage; code ported from another AR that assumed a key.
Related errors
- "{}" must have a primary key.
- Primary key of '{$class}' can not be empty.
- {class} does not have a primary key. You should either defin
- Key "{}" is not a column name and can not be used as a filte
- Invalid link: it must be an array of key-value pairs.
AI-assisted analysis of yiisoft/yii2@66f00d18a2 (2026-08-17).
Data as JSON: /api/errors/336eb3971761e704.
Report an issue: GitHub.