makeplane/plane · error · DRFValidationError
invalid_not_child
invalid_not_child
Error message
'not' must be a single JSON object
What it means
Raised by _validate_structure when a 'not' operator's value is not a dict. Unlike or/and (which take a list), 'not' takes a single JSON object that itself follows the filter grammar; lists, scalars, or null are rejected.
Source
Thrown at apps/api/plane/utils/filters/filter_backend.py:399
)
for child in value:
if not isinstance(child, dict):
raise DRFValidationError(
{
"message": f"All children of '{op}' must be JSON objects",
"code": "invalid_operator_child_type",
}
)
self._validate_structure(
child,
max_depth=max_depth,
current_depth=current_depth + 1,
)
return
if op == "not":
if not isinstance(value, dict):
raise DRFValidationError(
{
"message": "'not' must be a single JSON object",
"code": "invalid_not_child",
}
)
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",View on GitHub (pinned to 1c8a60f858)
Solutions
- Give 'not' a single object: {"not":{"state":"open"}}.
- To negate multiple conditions, wrap them in and/or inside the not: {"not":{"and":[...]}}.
- Use 'or'/'and' for multi-operand composition; reserve 'not' for unary negation.
Example fix
// before
?filters={"not":[{"state":"open"}]}
// after
?filters={"not":{"state":"open"}} Defensive patterns
Strategy: type-guard
Validate before calling
def assert_not_operand(node):
for k, v in node.items():
if isinstance(k, str) and k.lower() == 'not':
if not isinstance(v, dict):
raise ValueError("'not' operand must be an object") Type guard
function notOperandIsObject(node) {
const op = Object.keys(node).find(k => k.toLowerCase() === 'not');
return !op || (typeof node[op] === 'object' && node[op] !== null && !Array.isArray(node[op]));
} Prevention
- Reserve 'not' for unary negation — it takes one object, not a list.
- To negate multiple clauses, wrap them in and/or inside the not operand.
When it happens
Trigger: GET .../?filters={"not":[{"state":"open"}]} (array given to not); GET .../?filters={"not":"state"} (string); GET .../?filters={"not":null}; GET .../?filters={"not":[{"state":"open"},{"priority":"high"}]} (using not as if it were or).
Common situations: Treating 'not' symmetrically with or/and (which take arrays); client builder that always wraps operands in a list; confusing 'not' with a NAND-style multi-operand operator.
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/9d0ae64550348961.
Report an issue: GitHub.