{"record":{"id":"e3784bc641f3b5cd","repo":"yiisoft/yii2","slug":"invalid-validation-rule-a-rule-must-specify-both-e3784b","errorCode":null,"errorMessage":"Invalid validation rule: a rule must specify both attribute names and validator type.","messagePattern":"Invalid validation rule: a rule must specify both attribute names and validator type\\.","errorType":"exception","errorClass":"InvalidConfigException","httpStatus":null,"severity":"error","filePath":"framework/base/Model.php","lineNumber":488,"sourceCode":"    }\n\n    /**\n     * Creates validator objects based on the validation rules specified in [[rules()]].\n     * Unlike [[getValidators()]], each time this method is called, a new list of validators will be returned.\n     * @return ArrayObject<int, Validator> validators\n     * @throws InvalidConfigException if any validation rule configuration is invalid\n     */\n    public function createValidators()\n    {\n        $validators = new ArrayObject();\n        foreach ($this->rules() as $rule) {\n            if ($rule instanceof Validator) {\n                $validators->append($rule);\n            } elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type\n                $validator = Validator::createValidator($rule[1], $this, (array) $rule[0], array_slice($rule, 2));\n                $validators->append($validator);\n            } else {\n                throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.');\n            }\n        }\n\n        return $validators;\n    }\n\n    /**\n     * Returns a value indicating whether the attribute is required.\n     * This is determined by checking if the attribute is associated with a\n     * [[\\yii\\validators\\RequiredValidator|required]] validation rule in the\n     * current [[scenario]].\n     *\n     * Note that when the validator has a conditional validation applied using\n     * [[\\yii\\validators\\RequiredValidator::$when|$when]] this method will return\n     * `false` regardless of the `when` condition because it may be called be\n     * before the model is loaded with data.\n     *\n     * @param string $attribute attribute name","sourceCodeStart":470,"sourceCodeEnd":506,"githubUrl":"https://github.com/yiisoft/yii2/blob/66f00d18a29b520f85e8e8f1e32d1e7e7b556cac/framework/base/Model.php#L470-L506","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\npublic function rules()\n{\n    return [\n        [['email'], 'required'],\n        ['email'], // stray one-element rule: no validator type\n    ];\n}\n\n// after\npublic function rules()\n{\n    return [\n        [['email'], 'required'],\n        [['email'], 'email'],\n    ];\n}","handlingStrategy":"validation","validationCode":"foreach ((new $modelClass)->rules() as $rule) {\n    if (!isValidRuleShape($rule)) {\n        throw new \\InvalidConfigException(\"Malformed validation rule in $modelClass\");\n    }\n}","typeGuard":"function isValidRuleShape($rule): bool\n{\n    return $rule instanceof \\yii\\validators\\Validator\n        || (is_array($rule) && isset($rule[0], $rule[1]));\n}","tryCatchPattern":"try {\n    $model->validate();\n} catch (\\yii\\base\\InvalidConfigException $e) {\n    // rules() contains a malformed entry — surface the model class in CI, never retry silently\n}","preventionTips":["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"],"tags":["php","yii2","model","validation","rules","config"],"backgroundTag":"invalid-validation-rule","analyzedSha":"66f00d18a29b520f85e8e8f1e32d1e7e7b556cac","analyzedAt":"2026-08-17T05:17:23.470Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}