{"record":{"id":"e91db387ce5a2f93","repo":"makeplane/plane","slug":"invalid-operator-children","errorCode":"invalid_operator_children","errorMessage":"'{op}' must be a non-empty list of filter objects","messagePattern":"'(.+?)' must be a non-empty list of filter objects","errorType":"validation","errorClass":"DRFValidationError","httpStatus":400,"severity":"error","filePath":"apps/api/plane/utils/filters/filter_backend.py","lineNumber":376,"sourceCode":"            )\n\n        if len(logical_keys) == 1:\n            op_key = logical_keys[0]\n            # must not mix operator with other keys\n            if len(node) != 1:\n                raise DRFValidationError(\n                    {\n                        \"message\": (f\"Cannot mix logical operator '{op_key}' with field keys at the same level\"),\n                        \"code\": \"mixed_operator_and_fields\",\n                    }\n                )\n\n            op = op_key.lower()\n            value = node[op_key]\n\n            if op in (\"or\", \"and\"):\n                if not isinstance(value, list) or len(value) == 0:\n                    raise DRFValidationError(\n                        {\n                            \"message\": f\"'{op}' must be a non-empty list of filter objects\",\n                            \"code\": \"invalid_operator_children\",\n                        }\n                    )\n                for child in value:\n                    if not isinstance(child, dict):\n                        raise DRFValidationError(\n                            {\n                                \"message\": f\"All children of '{op}' must be JSON objects\",\n                                \"code\": \"invalid_operator_child_type\",\n                            }\n                        )\n                    self._validate_structure(\n                        child,\n                        max_depth=max_depth,\n                        current_depth=current_depth + 1,\n                    )","sourceCodeStart":358,"sourceCodeEnd":394,"githubUrl":"https://github.com/makeplane/plane/blob/1c8a60f858d8472aa56e29994ec1c7926da2c6ce/apps/api/plane/utils/filters/filter_backend.py#L358-L394","documentation":"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.","triggerScenarios":"GET .../?filters={\"or\":{}} (object instead of array); GET .../?filters={\"and\":[]} (empty array); GET .../?filters={\"or\":\"state\"} (string value); GET .../?filters={\"and\":null}.","commonSituations":"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.","solutions":["Use a non-empty array of objects: {\"or\":[{\"state\":\"open\"},{\"state\":\"closed\"}]}.","If you have only one clause, you do not need an operator wrapper at all — send the leaf directly.","Filter out empty operator groups before serializing."],"exampleFix":"// before\n?filters={\"or\":{}}\n// after\n?filters={\"or\":[{\"state\":\"open\"}]}","handlingStrategy":"type-guard","validationCode":"def assert_operator_children(node):\n    for k, v in node.items():\n        if isinstance(k, str) and k.lower() in ('or', 'and'):\n            if not isinstance(v, list) or len(v) == 0:\n                raise ValueError(f\"'{k}' must be a non-empty list\")","typeGuard":"function isNonEmptyListOfObjects(v) {\n  return Array.isArray(v) && v.length > 0 && v.every(x => typeof x === 'object' && x !== null && !Array.isArray(x));\n}","tryCatchPattern":null,"preventionTips":["Skip the or/and wrapper when there is only one clause — send the leaf directly.","Strip empty operator groups during client serialization."],"tags":["filters","schema","validation","api"],"backgroundTag":null,"analyzedSha":"1c8a60f858d8472aa56e29994ec1c7926da2c6ce","analyzedAt":"2026-08-12T14:44:31.636Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}