yiisoft/yii2 · error · InvalidConfigException
Invalid validation rule: a rule must specify both attribute
Error message
Invalid validation rule: a rule must specify both attribute names and validator type.
What it means
yii\base\Model::createValidators() walks the array returned by rules() and accepts only two entry shapes: a Validator instance, or an array whose positional offsets 0 and 1 are both set (attribute list, validator type). Any other entry — a one-element array, an array built with associative keys instead of positional ones, or a scalar — throws InvalidConfigException. It runs lazily on first validator access, usually the first validate() or isAttributeRequired() call.
Source
Thrown at framework/base/Model.php:488
}
/**
* Creates validator objects based on the validation rules specified in [[rules()]].
* Unlike [[getValidators()]], each time this method is called, a new list of validators will be returned.
* @return ArrayObject<int, Validator> validators
* @throws InvalidConfigException if any validation rule configuration is invalid
*/
public function createValidators()
{
$validators = new ArrayObject();
foreach ($this->rules() as $rule) {
if ($rule instanceof Validator) {
$validators->append($rule);
} elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type
$validator = Validator::createValidator($rule[1], $this, (array) $rule[0], array_slice($rule, 2));
$validators->append($validator);
} else {
throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.');
}
}
return $validators;
}
/**
* Returns a value indicating whether the attribute is required.
* This is determined by checking if the attribute is associated with a
* [[\yii\validators\RequiredValidator|required]] validation rule in the
* current [[scenario]].
*
* Note that when the validator has a conditional validation applied using
* [[\yii\validators\RequiredValidator::$when|$when]] this method will return
* `false` regardless of the `when` condition because it may be called be
* before the model is loaded with data.
*
* @param string $attribute attribute nameView on GitHub (pinned to 66f00d18a2)
Solutions
- Open rules() of the model in the stack trace's validate()/createValidators() frame and find the entry with fewer than two positional elements
- Rewrite every rule in the positional shape [attributesArray, 'validatorType', ...options]
- If the intent was 'make the attribute massive-assignable', use [['attr'], 'safe'] rather than dropping the validator type
- Add a unit test that calls createValidators() on every model so malformed rules fail in CI instead of production
Example fix
// before
public function rules()
{
return [
[['email'], 'required'],
['email'], // stray one-element rule: no validator type
];
}
// after
public function rules()
{
return [
[['email'], 'required'],
[['email'], 'email'],
];
} Defensive patterns
Strategy: validation
Validate before calling
foreach ((new $modelClass)->rules() as $rule) {
if (!isValidRuleShape($rule)) {
throw new \InvalidConfigException("Malformed validation rule in $modelClass");
}
} Type guard
function isValidRuleShape($rule): bool
{
return $rule instanceof \yii\validators\Validator
|| (is_array($rule) && isset($rule[0], $rule[1]));
} Try / catch
try {
$model->validate();
} catch (\yii\base\InvalidConfigException $e) {
// rules() contains a malformed entry — surface the model class in CI, never retry silently
} Prevention
- Keep rules in the positional [attributes, type] shape; never use named keys for offsets 0 and 1
- Unit-test createValidators() for every model so rule config errors fail in CI
- After merging rules() arrays (base + child), lint the merged list for one-element or scalar entries
When it happens
Trigger: A rules() entry like [['email']] (validator type missing) or ['required'] (attributes missing); a rule written with named keys ['attributes' => ['email'], 'validator' => 'required'] so isset($rule[0], $rule[1]) fails; a stray element left by misnested brackets, e.g. return [ [['email'], 'trim'], ['email'] ]; a plain string rule like 'required' appearing as its own element.
Common situations: Hand-editing a large rules() array and misplacing a closing bracket; merging rule arrays where one branch returns strings or partially built rules; copy-pasted rules from docs that use named keys; refactoring a rule into a variable and dropping the second element.
Related errors
- Unknown scenario: $scenario
- The directory does not exist: $path
- {cipher} is not an allowed cipher
- Failed to generate HMAC with hash algorithm: {macHash}
- There is no type mapping for '{attribute}'.
AI-assisted analysis of yiisoft/yii2@66f00d18a2 (2026-08-17).
Data as JSON: /api/errors/e3784bc641f3b5cd.
Report an issue: GitHub.