{"record":{"id":"76d5644161aaac4f","repo":"makeplane/plane","slug":"max-depth-exceeded","errorCode":"max_depth_exceeded","errorMessage":"Filter nesting is too deep (max {max_depth}); found depth {current_depth}","messagePattern":"Filter nesting is too deep \\(max (.+?)\\); found depth (.+?)","errorType":"validation","errorClass":"DRFValidationError","httpStatus":400,"severity":"warning","filePath":"apps/api/plane/utils/filters/filter_backend.py","lineNumber":327,"sourceCode":"            return value_int\n        except Exception:\n            return self.default_max_depth\n\n    def _validate_structure(self, node, max_depth, current_depth):\n        \"\"\"Validate JSON structure and enforce nesting depth.\n\n        Rules:\n        - Each object may contain only one logical operator:\n          or/and/not (case-insensitive)\n        - Logical operator objects cannot contain field keys alongside the\n          operator\n        - or/and values must be non-empty lists of dicts\n        - not value must be a dict\n        - Leaf objects must only contain field keys and acceptable values\n        - Depth must not exceed max_depth\n        \"\"\"\n        if current_depth > max_depth:\n            raise DRFValidationError(\n                {\n                    \"message\": (f\"Filter nesting is too deep (max {max_depth}); found depth {current_depth}\"),\n                    \"code\": \"max_depth_exceeded\",\n                }\n            )\n\n        if not isinstance(node, dict):\n            raise DRFValidationError(\n                {\n                    \"message\": \"Each filter node must be a JSON object\",\n                    \"code\": \"invalid_filter_node\",\n                }\n            )\n\n        if not node:\n            raise DRFValidationError(\n                {\n                    \"message\": \"Filter objects must not be empty\",","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/makeplane/plane/blob/1c8a60f858d8472aa56e29994ec1c7926da2c6ce/apps/api/plane/utils/filters/filter_backend.py#L309-L345","documentation":"Raised by _validate_structure when current_depth exceeds max_depth (default 5, overridable per-view via complex_filter_max_depth). The recursion counts every nested or/and/not operator level, so deeply composed boolean trees are capped to bound CPU and prevent stack blow-ups during validation.","triggerScenarios":"A filter with six or more levels of nested {'and':[{'or':[{'and':[...]}]}]}; deeply recursive 'not' chains like {'not':{'not':{'not':{'not':{'not':{'not':{...}}}}}}}; a programmatic client that wraps every condition in an extra and-group.","commonSituations":"Auto-generated filter trees (e.g. translating a UI rule builder) that add a redundant wrapper per clause; adversarial input; migrating from a less strict filter backend that allowed arbitrary depth.","solutions":["Flatten redundant nesting: {'and':[{'and':[...]}]} collapses to {'and':[...]}.","Raise the cap only if justified: set `complex_filter_max_depth = 8` on the view.","Audit the client's filter-tree builder to stop wrapping single-child groups."],"exampleFix":"// before\n?filters={\"and\":[{\"and\":[{\"and\":[{\"and\":[{\"and\":[{\"state\":\"open\"}]}]}]}]}]}\n// after\n?filters={\"state\":\"open\"}","handlingStrategy":"validation","validationCode":"MAX_DEPTH = getattr(view, 'complex_filter_max_depth', 5)\n\ndef depth(node, d=1):\n    if not isinstance(node, dict):\n        return d\n    op_keys = [k for k in node if isinstance(k, str) and k.lower() in ('or', 'and', 'not')]\n    if not op_keys:\n        return d\n    children = node[op_keys[0]]\n    kids = children if isinstance(children, list) else [children]\n    return max((depth(c, d + 1) for c in kids), default=d)\n\nif depth(filter_data) > MAX_DEPTH:\n    raise ValueError(f'filter tree too deep (max {MAX_DEPTH})')","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Flatten single-child and/or groups in the client before sending.","If the cap is too low for legitimate use, raise complex_filter_max_depth on the view with a justification."],"tags":["filters","depth-limit","validation","api"],"backgroundTag":null,"analyzedSha":"1c8a60f858d8472aa56e29994ec1c7926da2c6ce","analyzedAt":"2026-08-12T14:44:31.636Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}