makeplane/plane · error · DRFValidationError

filtering_not_enabled

filtering_not_enabled

Error message

Filtering is not enabled for this endpoint (missing filterset_class)

What it means

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.

Source

Thrown at apps/api/plane/utils/filters/filter_backend.py:106

        # Validate against the view's FilterSet (only declared filters are allowed)
        self._validate_fields(filter_data, view)

        # Build combined Q object from the filter tree
        combined_q = self._evaluate_node(filter_data, view, queryset)
        if combined_q is None:
            return queryset

        # Apply the combined Q object to the queryset once
        return queryset.filter(combined_q)

    def _validate_fields(self, filter_data, view):
        """Validate that filtered fields are defined in the view's FilterSet."""
        filterset_class = getattr(view, "filterset_class", None)
        allowed_fields = set(filterset_class.base_filters.keys()) if filterset_class else None
        if not allowed_fields:
            # If no FilterSet is configured, reject filtering to avoid unintended exposure # noqa: E501
            raise DRFValidationError(
                {
                    "message": ("Filtering is not enabled for this endpoint (missing filterset_class)"),
                    "code": "filtering_not_enabled",
                }
            )

        # Extract field names from the filter data
        fields = self._extract_field_names(filter_data)

        # Check if all fields are allowed
        for field in fields:
            # Field keys must match FilterSet filter names (including any lookups)
            # Example: 'sequence_id__gte' should be declared in base_filters
            # Special-case __range: require the '<base>__range' filter itself
            if field not in allowed_fields:
                raise DRFValidationError(
                    {
                        "message": f"Filtering on field '{field}' is not allowed",

View on GitHub (pinned to 1c8a60f858)

Solutions

  1. Add a filterset_class to the view: `class MyView(...): filterset_class = MyFilterSet; filter_backends = (ComplexFilterBackend,)`.
  2. If the endpoint genuinely should not support filtering, remove ComplexFilterBackend from filter_backends so the param is ignored entirely.
  3. Verify the import path of the filterset is correct and the attribute is not shadowed by None.

Example fix

# before
class CycleIssueView(BaseViewSet):
    filter_backends = (ComplexFilterBackend,)

# after
class CycleIssueView(BaseViewSet):
    filter_backends = (ComplexFilterBackend,)
    filterset_class = IssueFilterSet
Defensive patterns

Strategy: validation

Validate before calling

def assert_view_filterable(view):
    fs = getattr(view, 'filterset_class', None)
    assert fs is not None and hasattr(fs, 'base_filters'), (
        f'{type(view).__name__} uses ComplexFilterBackend but has no filterset_class'
    )

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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