{"record":{"id":"fd17c20c56ff3ab1","repo":"yiisoft/yii2","slug":"operator-operator-requires-three-operands-fd17c2","errorCode":null,"errorMessage":"Operator '$operator' requires three operands.","messagePattern":"Operator '\\$operator' requires three operands\\.","errorType":"exception","errorClass":"yii\\base\\InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"framework/db/conditions/BetweenCondition.php","lineNumber":95,"sourceCode":"        return $this->intervalStart;\n    }\n\n    /**\n     * @return mixed\n     */\n    public function getIntervalEnd()\n    {\n        return $this->intervalEnd;\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 (!isset($operands[0], $operands[1], $operands[2])) {\n            throw new InvalidArgumentException(\"Operator '$operator' requires three operands.\");\n        }\n\n        return new static($operands[0], $operator, $operands[1], $operands[2]);\n    }\n}\n","sourceCodeStart":77,"sourceCodeEnd":101,"githubUrl":"https://github.com/yiisoft/yii2/blob/66f00d18a29b520f85e8e8f1e32d1e7e7b556cac/framework/db/conditions/BetweenCondition.php#L77-L101","documentation":"Thrown by yii\\db\\conditions\\BetweenCondition::fromArrayDefinition() (framework/db/conditions/BetweenCondition.php:95) when a BETWEEN/NOT BETWEEN condition in operator-array format does not have all three operands (column, min, max) set. QueryBuilder::createConditionFromArray() (framework/db/QueryBuilder.php:1600-1610) routes every ['between', ...] / ['not between', ...] where() array here, so the error surfaces when the SQL is built (e.g. at ->all() or ->createCommand()), not when where() is called. Because the guard uses isset(), a null operand (e.g. a null $max bound) fails exactly like a missing one.","triggerScenarios":"Calling ->where(['between','price',10]) or ->andWhere(['not between','created_at',$from,$to]) with a missing bound; building range filters from user input where one bound is absent or null; passing ['between','col',null,10] — isset() treats the null slot as unset.","commonSituations":"Search/filter forms with optional min/max fields where empty input becomes null; refactoring a raw SQL BETWEEN into array format and forgetting one operand; note that andFilterWhere() is safe here — filterCondition (framework/db/QueryTrait.php:270-276) drops the whole BETWEEN condition when either bound is empty.","solutions":["Provide all three operands: ['between', 'column', $min, $max]","When bounds are optional, only add the condition when both are non-null; otherwise fall back to ['>','col',$min] / ['<','col',$max]","When assembling conditions dynamically, verify operand count before appending the operator element"],"exampleFix":"// before\n$items = (new Query())->from('product')\n    ->where(['between', 'price', $min, $max]) // throws when $min or $max is null (isset)\n    ->all();\n\n// after\n$query = (new Query())->from('product');\nif ($min !== null && $max !== null) {\n    $query->andWhere(['between', 'price', $min, $max]);\n} else {\n    if ($min !== null) { $query->andWhere(['>', 'price', $min]); }\n    if ($max !== null) { $query->andWhere(['<', 'price', $max]); }\n}\n$items = $query->all();","handlingStrategy":"validation","validationCode":"function hasBetweenOperands(array $condition): bool\n{\n    // operator at 0, then column, min, max — all set and non-null\n    return isset($condition[1], $condition[2], $condition[3])\n        && $condition[2] !== null\n        && $condition[3] !== null;\n}\n\n// usage\nif (hasBetweenOperands($where)) {\n    $query->andWhere($where);\n}","typeGuard":null,"tryCatchPattern":"try {\n    $rows = $query->createCommand()->queryAll(); // condition is compiled here\n} catch (\\yii\\base\\InvalidArgumentException $e) {\n    \\Yii::warning('Malformed BETWEEN condition: ' . $e->getMessage(), __METHOD__);\n    $rows = [];\n}","preventionTips":["Normalize filter input before building where() arrays: empty request values become null — skip the condition instead of passing it","Remember isset() treats null as missing; a null bound fails the guard exactly like an absent one","Unit-test every dynamic filter with the 'all bounds empty' case"],"tags":["yii2","database","query-builder","between","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"}