{"record":{"id":"b8a3e5dda25d027a","repo":"makeplane/plane","slug":"filtering-not-enabled","errorCode":"filtering_not_enabled","errorMessage":"Filtering is not enabled for this endpoint (missing filterset_class)","messagePattern":"Filtering is not enabled for this endpoint \\(missing filterset_class\\)","errorType":"validation","errorClass":"DRFValidationError","httpStatus":400,"severity":"error","filePath":"apps/api/plane/utils/filters/filter_backend.py","lineNumber":106,"sourceCode":"\n        # Validate against the view's FilterSet (only declared filters are allowed)\n        self._validate_fields(filter_data, view)\n\n        # Build combined Q object from the filter tree\n        combined_q = self._evaluate_node(filter_data, view, queryset)\n        if combined_q is None:\n            return queryset\n\n        # Apply the combined Q object to the queryset once\n        return queryset.filter(combined_q)\n\n    def _validate_fields(self, filter_data, view):\n        \"\"\"Validate that filtered fields are defined in the view's FilterSet.\"\"\"\n        filterset_class = getattr(view, \"filterset_class\", None)\n        allowed_fields = set(filterset_class.base_filters.keys()) if filterset_class else None\n        if not allowed_fields:\n            # If no FilterSet is configured, reject filtering to avoid unintended exposure # noqa: E501\n            raise DRFValidationError(\n                {\n                    \"message\": (\"Filtering is not enabled for this endpoint (missing filterset_class)\"),\n                    \"code\": \"filtering_not_enabled\",\n                }\n            )\n\n        # Extract field names from the filter data\n        fields = self._extract_field_names(filter_data)\n\n        # Check if all fields are allowed\n        for field in fields:\n            # Field keys must match FilterSet filter names (including any lookups)\n            # Example: 'sequence_id__gte' should be declared in base_filters\n            # Special-case __range: require the '<base>__range' filter itself\n            if field not in allowed_fields:\n                raise DRFValidationError(\n                    {\n                        \"message\": f\"Filtering on field '{field}' is not allowed\",","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/makeplane/plane/blob/1c8a60f858d8472aa56e29994ec1c7926da2c6ce/apps/api/plane/utils/filters/filter_backend.py#L88-L124","documentation":"Raised by _validate_fields when the view exposes ComplexFilterBackend in filter_backends but does not declare a filterset_class attribute (or it has no base_filters). This is a fail-closed guard: the backend refuses to apply any user-supplied filter when no allowlist exists, preventing unintended data exposure.","triggerScenarios":"A developer adds `filter_backends = (ComplexFilterBackend,)` to a view (e.g. a new CycleIssueView or ModuleIssueView subclass) but forgets to add `filterset_class = SomeFilterSet`. Any GET to that endpoint with a `?filters=...` param returns 400 with filtering_not_enabled.","commonSituations":"New view scaffolding; removing a filterset during a refactor; subclassing an existing view and overriding filter_backends without redeclaring filterset_class; mis-importing the filterset so the attribute is None.","solutions":["Add a filterset_class to the view: `class MyView(...): filterset_class = MyFilterSet; filter_backends = (ComplexFilterBackend,)`.","If the endpoint genuinely should not support filtering, remove ComplexFilterBackend from filter_backends so the param is ignored entirely.","Verify the import path of the filterset is correct and the attribute is not shadowed by None."],"exampleFix":"# before\nclass CycleIssueView(BaseViewSet):\n    filter_backends = (ComplexFilterBackend,)\n\n# after\nclass CycleIssueView(BaseViewSet):\n    filter_backends = (ComplexFilterBackend,)\n    filterset_class = IssueFilterSet","handlingStrategy":"validation","validationCode":"def assert_view_filterable(view):\n    fs = getattr(view, 'filterset_class', None)\n    assert fs is not None and hasattr(fs, 'base_filters'), (\n        f'{type(view).__name__} uses ComplexFilterBackend but has no filterset_class'\n    )","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat filter_backends and filterset_class as a pair: adding one requires the other.","Add a test that iterates views with ComplexFilterBackend in filter_backends and asserts filterset_class is set."],"tags":["filters","config","view-config","api"],"backgroundTag":null,"analyzedSha":"1c8a60f858d8472aa56e29994ec1c7926da2c6ce","analyzedAt":"2026-08-12T14:44:31.636Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}