makeplane/plane · error · DRFValidationError

invalid_operator_children

invalid_operator_children

Error message

'{op}' must be a non-empty list of filter objects

What it means

Raised by _validate_structure when an 'or' or 'and' operator's value is not a list, or is an empty list. The grammar requires or/and to take a non-empty JSON array of objects so the backend always has at least one child to evaluate.

Source

Thrown at apps/api/plane/utils/filters/filter_backend.py:376

            )

        if len(logical_keys) == 1:
            op_key = logical_keys[0]
            # must not mix operator with other keys
            if len(node) != 1:
                raise DRFValidationError(
                    {
                        "message": (f"Cannot mix logical operator '{op_key}' with field keys at the same level"),
                        "code": "mixed_operator_and_fields",
                    }
                )

            op = op_key.lower()
            value = node[op_key]

            if op in ("or", "and"):
                if not isinstance(value, list) or len(value) == 0:
                    raise DRFValidationError(
                        {
                            "message": f"'{op}' must be a non-empty list of filter objects",
                            "code": "invalid_operator_children",
                        }
                    )
                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,
                    )

View on GitHub (pinned to 1c8a60f858)

Solutions

  1. Use a non-empty array of objects: {"or":[{"state":"open"},{"state":"closed"}]}.
  2. If you have only one clause, you do not need an operator wrapper at all — send the leaf directly.
  3. Filter out empty operator groups before serializing.

Example fix

// before
?filters={"or":{}}
// after
?filters={"or":[{"state":"open"}]}
Defensive patterns

Strategy: type-guard

Validate before calling

def assert_operator_children(node):
    for k, v in node.items():
        if isinstance(k, str) and k.lower() in ('or', 'and'):
            if not isinstance(v, list) or len(v) == 0:
                raise ValueError(f"'{k}' must be a non-empty list")

Type guard

function isNonEmptyListOfObjects(v) {
  return Array.isArray(v) && v.length > 0 && v.every(x => typeof x === 'object' && x !== null && !Array.isArray(x));
}

Prevention

When it happens

Trigger: GET .../?filters={"or":{}} (object instead of array); GET .../?filters={"and":[]} (empty array); GET .../?filters={"or":"state"} (string value); GET .../?filters={"and":null}.

Common situations: Treating 'and'/'or' as objects keyed by field name; serialization that emits [] when a clause list is empty; client sets the value to None when no clauses are added.

Related errors


AI-assisted analysis of makeplane/plane@1c8a60f858 (2026-08-12). Data as JSON: /api/errors/e91db387ce5a2f93. Report an issue: GitHub.