makeplane/plane · error · DRFValidationError
operator_in_leaf
operator_in_leaf
Error message
Logical operators cannot appear in a leaf filter object
What it means
Raised by _validate_leaf when a leaf object contains a key whose lowercase form is 'or', 'and', or 'not'. After structural dispatch, a leaf should contain only field keys; the presence of a reserved operator word inside a leaf means the schema was mis-assembled (the operator should have been at a parent level).
Source
Thrown at apps/api/plane/utils/filters/filter_backend.py:423
self._validate_structure(value, max_depth=max_depth, current_depth=current_depth + 1)
return
# Leaf node: validate fields and values
self._validate_leaf(node)
def _validate_leaf(self, leaf):
"""Validate a leaf dict containing field lookups and values."""
if not isinstance(leaf, dict) or not leaf:
raise DRFValidationError(
{
"message": "Leaf filter must be a non-empty JSON object",
"code": "invalid_leaf",
}
)
for key, value in leaf.items():
if isinstance(key, str) and key.lower() in ("or", "and", "not"):
raise DRFValidationError(
{
"message": "Logical operators cannot appear in a leaf filter object",
"code": "operator_in_leaf",
}
)
# Lists/Tuples must contain only scalar values
if isinstance(value, (list, tuple)):
if len(value) == 0:
raise DRFValidationError(
{
"message": f"List value for '{key}' must not be empty",
"code": "empty_list_value",
}
)
for item in value:
if not self._is_scalar(item):
raise DRFValidationError(View on GitHub (pinned to 1c8a60f858)
Solutions
- Move the operator to its own parent level so the leaf contains only field keys.
- If a real field is named 'and'/'or'/'not', rename it or wrap it under a different key; the grammar reserves these words case-insensitively.
- Validate client-side that no field name lower-cases to an operator keyword.
Example fix
// before
?filters={"state":"open","and":"priority"}
// after
?filters={"and":[{"state":"open"},{"priority":"high"}]} Defensive patterns
Strategy: validation
Validate before calling
RESERVED = {'or', 'and', 'not'}
def assert_no_reserved_in_leaf(leaf):
for k in leaf:
if isinstance(k, str) and k.lower() in RESERVED:
raise ValueError(f"'{k}' is reserved and cannot be a field name in a leaf") Type guard
const RESERVED = new Set(['or','and','not']);
function leafHasNoReservedKeys(leaf) {
return Object.keys(leaf).every(k => !RESERVED.has(k.toLowerCase()));
} Prevention
- Rename real fields that collide with or/and/not; matching is case-insensitive.
- Place operator keywords at the parent level, never alongside field keys.
When it happens
Trigger: GET .../?filters={"state":"open","and":"something"} (and treated as a field name inside a leaf); GET .../?filters={"OR":{...}} where OR is mis-cased inside a leaf context; clients that allow operator keywords as field names.
Common situations: Custom-property or column named 'and'/'or'/'not' that the client forwards as a field key; merging an operator fragment into a leaf dict; case variations (Or, AND) the client assumed would be treated as fields.
Related errors
- invalid_filter_node
- empty_filter_object
- multiple_logical_operators
- mixed_operator_and_fields
- invalid_operator_children
AI-assisted analysis of makeplane/plane@1c8a60f858 (2026-08-12).
Data as JSON: /api/errors/89d8fb4474e28ee6.
Report an issue: GitHub.