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

  1. Move the operator to its own parent level so the leaf contains only field keys.
  2. 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.
  3. 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

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


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