{"record":{"id":"f7a7430af790cc32","repo":"makeplane/plane","slug":"mixed-operator-and-fields","errorCode":"mixed_operator_and_fields","errorMessage":"Cannot mix logical operator '{op_key}' with field keys at the same level","messagePattern":"Cannot mix logical operator '(.+?)' with field keys at the same level","errorType":"validation","errorClass":"DRFValidationError","httpStatus":400,"severity":"error","filePath":"apps/api/plane/utils/filters/filter_backend.py","lineNumber":364,"sourceCode":"                    \"code\": \"empty_filter_object\",\n                }\n            )\n\n        logical_keys = [k for k in node.keys() if isinstance(k, str) and k.lower() in (\"or\", \"and\", \"not\")]\n\n        if len(logical_keys) > 1:\n            raise DRFValidationError(\n                {\n                    \"message\": (\"A filter object cannot contain multiple logical operators at the same level\"),\n                    \"code\": \"multiple_logical_operators\",\n                }\n            )\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:","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/makeplane/plane/blob/1c8a60f858d8472aa56e29994ec1c7926da2c6ce/apps/api/plane/utils/filters/filter_backend.py#L346-L382","documentation":"Raised by _validate_structure when a node has exactly one logical-operator key AND at least one other key. The grammar requires the operator object to contain *only* the operator — fields must live in a child object, never alongside or/and/not at the same level.","triggerScenarios":"GET .../?filters={\"or\":[{\"state\":\"open\"}],\"priority\":\"high\"} ; GET .../?filters={\"state\":\"open\",\"not\":{\"priority\":\"low\"}}.","commonSituations":"Adding a quick field filter to an existing operator object via spread/merge; misunderstanding that fields cannot ride alongside the operator; legacy converter that flattens fields into the operator node.","solutions":["Move sibling fields into a child of the operator: {\"and\":[{\"or\":[...]},{\"priority\":\"high\"}]}.","When building objects programmatically, never dict.update an operator object with field keys.","Treat operator objects as structural only — they hold either an or/and/not key or field keys, never both."],"exampleFix":"// before\n?filters={\"or\":[{\"state\":\"open\"}],\"priority\":\"high\"}\n// after\n?filters={\"and\":[{\"or\":[{\"state\":\"open\"}]},{\"priority\":\"high\"}]}","handlingStrategy":"validation","validationCode":"def assert_no_operator_field_mix(node):\n    op_keys = [k for k in node if isinstance(k, str) and k.lower() in ('or','and','not')]\n    if op_keys and len(node) > 1:\n        raise ValueError(f'operator {op_keys[0]} cannot share a node with field keys')","typeGuard":"function operatorNodeIsPure(node) {\n  const keys = Object.keys(node);\n  const op = keys.find(k => ['or','and','not'].includes(k.toLowerCase()));\n  return !op || keys.length === 1;\n}","tryCatchPattern":null,"preventionTips":["Treat operator nodes as structural wrappers — never update() field keys into them.","Place field filters inside a child of the operator."],"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"}