apache/superset · error · ValidationError

Regular RLS filters require at least one subject.

Error message

Regular RLS filters require at least one subject.

What it means

ValidationError raised in UpdateRLSRuleCommand.validate when the effective filter_type (payload value or the rule's stored value) resolves to REGULAR while the effective subjects list (payload 'subjects' or the rule's existing bindings) is empty — a regular RLS filter must keep at least one subject.

Source

Thrown at superset/commands/security/update.py:114

        # in the request body. A partial update (e.g. changing only the name)
        # must leave the rule's existing tables/subjects bindings untouched
        # rather than replacing them with empty lists.
        if "subjects" in self._properties:
            subjects = populate_subject_list(
                self._subjects,
                default_to_user=False,
            )
            self._properties["subjects"] = subjects
        else:
            subjects = list(self._model.subjects)

        filter_type = self._properties.get("filter_type", self._model.filter_type)
        filter_type_value = getattr(filter_type, "value", filter_type)
        if (
            filter_type_value == RowLevelSecurityFilterType.REGULAR.value
            and not subjects
        ):
            raise ValidationError(
                {"subjects": ["Regular RLS filters require at least one subject."]}
            )

View on GitHub (pinned to f4587218dd)

Solutions

  1. Include a non-empty 'subjects' array in the same PUT (e.g. a role subject) whenever filter_type is Regular
  2. Alternatively switch filter_type to 'Base' if the clause must be subject-independent
  3. Read the rule first (GET) to know the stored subjects/type your partial update will combine with

Example fix

# before
client.put(url, json={'filter_type': 'Regular'})  # rule has no subjects

# after
client.put(url, json={'filter_type': 'Regular',
                       'subjects': [{'subject_type': 'Role', 'subject': 'Gamma'}]})
Defensive patterns

Strategy: validation

Validate before calling

eff_type = props.get('filter_type', stored_rule['filter_type'])
eff_subjects = props['subjects'] if 'subjects' in props else stored_rule['subjects']
if eff_type == 'Regular':
    assert eff_subjects, 'Regular rules need >=1 subject'

Type guard

def update_keeps_regular_subjects(props: dict, stored: dict) -> bool:
    ftype = props.get('filter_type', stored.get('filter_type'))
    ftype = getattr(ftype, 'value', ftype)
    subjects = props['subjects'] if 'subjects' in props else stored.get('subjects', [])
    return ftype != 'Regular' or bool(subjects)

Try / catch

from superset.commands.exceptions import ValidationError
try:
    UpdateRLSRuleCommand(rid, props).run()
except ValidationError as e:
    if 'subjects' in e.normalized_messages():
        props['subjects'] = [{'subject_type': 'Role', 'subject': 'Gamma'}]
        UpdateRLSRuleCommand(rid, props).run()

Prevention

When it happens

Trigger: PUT switching filter_type to 'Regular' on a rule with no subjects; PUT with 'subjects': [] on a Regular rule; a partial update that only changes filter_type while the stored rule has no subject bindings (subjects then default to the empty stored list).

Common situations: Promoting a Base filter to Regular without adding subjects; clearing subjects to 'reset' a rule; partial payloads whose defaults surprise the author.

Related errors


AI-assisted analysis of apache/superset@f4587218dd (2026-08-14). Data as JSON: /api/errors/5156cc0768acf8f9. Report an issue: GitHub.