{"record":{"id":"a2bbd2da58e379ed","repo":"yiisoft/yii2","slug":"operator-operator-requires-exactly-one-operand","errorCode":null,"errorMessage":"Operator '$operator' requires exactly one operand.","messagePattern":"Operator '\\$operator' requires exactly one operand\\.","errorType":"exception","errorClass":"yii\\base\\InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"framework/db/conditions/NotCondition.php","lineNumber":53,"sourceCode":"        $this->condition = $condition;\n    }\n\n    /**\n     * @return mixed\n     */\n    public function getCondition()\n    {\n        return $this->condition;\n    }\n\n    /**\n     * {@inheritdoc}\n     * @throws InvalidArgumentException if wrong number of operands have been given.\n     */\n    public static function fromArrayDefinition($operator, $operands)\n    {\n        if (count($operands) !== 1) {\n            throw new InvalidArgumentException(\"Operator '$operator' requires exactly one operand.\");\n        }\n\n        return new static(array_shift($operands));\n    }\n}\n","sourceCodeStart":35,"sourceCodeEnd":59,"githubUrl":"https://github.com/yiisoft/yii2/blob/66f00d18a29b520f85e8e8f1e32d1e7e7b556cac/framework/db/conditions/NotCondition.php#L35-L59","documentation":"yii\\db\\conditions\\NotCondition::fromArrayDefinition() (framework/db/conditions/NotCondition.php:53) builds ['not', X] conditions and requires count($operands) === 1 — exactly one condition to negate. Unlike the isset()-based guards in the other condition classes, a null operand is acceptable here; extra elements or none at all are not.","triggerScenarios":"['not'] with nothing to negate; ['not', ['a'=>1], ['b'=>2]] passing several conditions; programmatically negating a dynamically built list where array_merge/array flattening turns one nested condition into multiple operands.","commonSituations":"Developers expecting NOT to accept a list like AND/OR do; building ['not', ...$conditions] spreads multiple operands after the operator; negating filter input that may be an empty array (['not', []] passes count===1 and builds NOT (1=0)? — no, it builds NOT of an empty condition, which yields unexpected SQL rather than an exception).","solutions":["Wrap exactly one condition: ->where(['not', ['status' => 'deleted']])","To negate several conditions, nest them first: ['not', ['and', ['a'=>1], ['b'=>2]]] or ['not', ['or', ...$conds]]","When negating dynamic input, build the inner condition separately and wrap it once: ['not', $inner]"],"exampleFix":"// before\n$cond = ['not', ['status' => 'draft'], ['archived' => 1]]; // 2 operands -> throws\n\n// after\n$cond = ['not', ['and', ['status' => 'draft'], ['archived' => 1]]];","handlingStrategy":"validation","validationCode":"function buildNotCondition(array $inner): array\n{\n    if (count($inner) === 0) {\n        return []; // nothing to negate\n    }\n    // collapse a list of conditions into ONE operand for NOT\n    $operand = count($inner) === 1\n        ? $inner[0]\n        : array_merge(['and'], $inner);\n\n    return ['not', $operand];\n}\n\n$query->andWhere(buildNotCondition($conditions));","typeGuard":null,"tryCatchPattern":"try {\n    $rows = $query->all();\n} catch (\\yii\\base\\InvalidArgumentException $e) {\n    \\Yii::warning('Malformed NOT condition: ' . $e->getMessage(), __METHOD__);\n    $rows = [];\n}","preventionTips":["NOT takes exactly one nested condition — wrap multiple conditions in ['and', ...] or ['or', ...] first","Never spread arrays into ['not', ...$conds]","Guard dynamic negation with a count() check before adding the operator"],"tags":["yii2","database","query-builder","not-condition","where-condition","validation"],"backgroundTag":"malformed-query-condition","analyzedSha":"66f00d18a29b520f85e8e8f1e32d1e7e7b556cac","analyzedAt":"2026-08-17T05:17:23.470Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}