HumanSignal/label-studio · error · ValueError

Child filters cannot contain nested child filters

Error message

Child filters cannot contain nested child filters

What it means

Pydantic model validator validate_one_nesting_level on the filter-group schema enforces that filters may be nested at most one level deep: if any filter's child_filters themselves contain child_filters, a ValueError('Child filters cannot contain nested child filters') is raised during request-body validation.

Source

Thrown at label_studio/data_manager/prepare_params.py:61

    @child_filter.setter
    def child_filter(self, value: Optional['Filter']) -> None:
        self.child_filters = [] if value is None else [value]


class ConjunctionEnum(Enum):
    OR = 'or'
    AND = 'and'


class Filters(BaseModel):
    conjunction: ConjunctionEnum
    items: List[Filter]

    @model_validator(mode='after')
    def validate_one_nesting_level(self):
        for item in self.items:
            if any(child.child_filters for child in item.child_filters):
                raise ValueError('Child filters cannot contain nested child filters')
        return self


class SelectedItems(BaseModel):
    all: bool
    included: List[int] = []
    excluded: List[int] = []


class PrepareParams(BaseModel):
    project: Union[int, List[int]]  # Support both single project and multiple projects
    ordering: List[str] = []
    selectedItems: Optional[SelectedItems] = None
    filters: Optional[Filters] = None
    data: Optional[dict] = None
    request: Optional[Any] = None

    @property

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Flatten the filter tree to at most one nesting level before submitting — move grandchild conditions into a sibling filter group.
  2. Serialize conditions as multiple top-level filters combined by the group's conjunction.
  3. Guard the filter-building code so appended child_filters are always leaf filters.
  4. Wrap view submission in try/except ValueError (pydantic ValidationError) and show a nesting-depth message.
  5. Inspect the saved view payload (e.g. via API GET) and manually fix the nested structure in the DB/export.

Example fix

// before
{"items": [{"field": "f1", "child_filters": [{"field": "f2", "child_filters": [{"field": "f3", "child_filters": []}]}]}]}
// after
{"items": [{"field": "f1", "child_filters": [{"field": "f2", "child_filters": []}]}, {"field": "f3", "child_filters": []}]}
Defensive patterns

Strategy: validation

Validate before calling

def enforce_single_nesting(filters):
    for item in filters.get('items', []):
        for child in item.get('child_filters', []):
            if child.get('child_filters'):
                child['child_filters'] = []  # flatten or reject
    return filters

Type guard

def is_one_level(filter_item: dict) -> bool:
    return not any(c.get('child_filters') for c in filter_item.get('child_filters', []))

Try / catch

from pydantic import ValidationError
try:
    params = PrepareParams.model_validate(request.data)
except ValidationError as e:
    if 'nested child filters' in str(e):
        payload['filters'] = enforce_single_nesting(payload['filters'])
        params = PrepareParams.model_validate(payload)
    else:
        raise

Prevention

When it happens

Trigger: Submitting a data manager view (POST/PATCH) whose filters JSON has a filter_group containing a filter with child_filters, and at least one of those child filters also has non-empty child_filters (grandchildren).

Common situations: Frontend builds a recursive/nested filter UI and sends the whole tree; a view JSON was hand-edited or migrated from a version allowing deeper nesting; filters from saved views with old schemas are replayed.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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