HumanSignal/label-studio · error · ValidationError

User filter "{field_name}" does not support operator "{opera

Error message

User filter "{field_name}" does not support operator "{operator}". Allowed: {allowed_label}.

What it means

validate_user_filter_operator checks a scalar-valued user filter's operator against the per-field allowlist returned by allowed_user_filter_operators(field_name); legacy operators in LEGACY_USER_FILTER_OPERATORS are still tolerated. If the operator is not allowed for that field, ValidationError is raised naming the field, the operator, and the allowed set.

Source

Thrown at label_studio/data_manager/managers.py:116

    can recover historical views.
    """
    if field_name not in USER_FILTER_FIELDS:
        return

    if isinstance(value, list):
        if operator not in USER_FILTER_VALUE_OPERATORS:
            allowed = ', '.join(sorted(USER_FILTER_VALUE_OPERATORS))
            raise ValidationError(f'List-valued user filters support only these operators: {allowed}.')
        return

    allowed = allowed_user_filter_operators(field_name)
    if operator in allowed:
        return
    if operator in LEGACY_USER_FILTER_OPERATORS:
        return

    allowed_label = ', '.join(sorted(allowed))
    raise ValidationError(
        f'User filter "{field_name}" does not support operator "{operator}". Allowed: {allowed_label}.'
    )


def normalize_persisted_user_filter(field_name, operator, value):
    """Recover historical user-filter shapes without relaxing validation for new writes."""
    if field_name not in USER_FILTER_FIELDS:
        return operator, value
    if operator == Operator.EMPTY:
        if field_name not in USER_FILTER_EMPTY_FIELDS:
            # Drop unsupported empty (e.g. skipped_by_annotator) to a no-op contains.
            return Operator.CONTAINS, []
        try:
            empty_value = cast_bool_from_str(value)
        except ValueError:
            return Operator.CONTAINS, []
        if isinstance(empty_value, bool):
            return operator, empty_value

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Use one of the operators listed in the error message for that field
  2. Consult allowed_user_filter_operators(field) to build the operator menu dynamically
  3. If a legacy operator was intended, keep it as-is — legacy names are still accepted
  4. Update stale saved views/filters that embed now-disallowed operators

Example fix

// before
{"filter": "filter:tasks:created_by", "operator": "in_list", "value": 3}
// after
{"filter": "filter:tasks:created_by", "operator": "equal", "value": 3}
Defensive patterns

Strategy: validation

Validate before calling

from data_manager.managers import allowed_user_filter_operators, LEGACY_USER_FILTER_OPERATORS
allowed = sorted(allowed_user_filter_operators(field)) + sorted(LEGACY_USER_FILTER_OPERATORS)
assert f['operator'] in allowed, f"{f['operator']} not allowed for {field}"

Type guard

def operator_allowed(field, op): return op in allowed_user_filter_operators(field) or op in LEGACY_USER_FILTER_OPERATORS

Try / catch

try:
    apply_filters(...)
except ValidationError as e:
    if 'does not support operator' in str(e): rebuild_filter_with_allowed_operator(e)
    else: raise

Prevention

When it happens

Trigger: Sending a filter like {filter: 'filter:tasks:created_by', operator: 'in_list', value: 3} or any operator not in that field's allowlist through apply_filters/validate().

Common situations: Client UI offering operators the backend doesn't support for a field; copied filter payloads reused across different field types; version drift after operator allowlists were tightened.

Related errors


AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29). Data as JSON: /api/errors/a48d5825d534449f. Report an issue: GitHub.