apache/superset · error · ValueError
Relationship filter on '{col_name}' requires a single-column
Error message
Relationship filter on '{col_name}' requires a single-column primary key on the related model; found {len(pk_cols)} columns. What it means
Raised by BaseDAO._apply_relationship_filter() when filtering on a collection relationship (many-to-many / one-to-many detected via RelationshipProperty.uselist) whose related model has a composite (multi-column) primary key. The implementation translates the operator into `.any(related_pk ...)` and needs exactly one PK column; it fails loudly with a ValueError rather than silently dropping trailing PK columns.
Source
Thrown at superset/daos/base.py:667
col_name: str,
operator_enum: "ColumnOperatorEnum",
value: Any,
) -> Any:
"""Apply a filter on a many-to-many or one-to-many relationship column.
Translates the caller's operator into a SQLAlchemy ``.any()``
expression against the related model's primary key. Supports
eq / ne / in / nin / is_null / is_not_null. Other operators
(sw, like, gt, etc.) don't make sense on a collection of related
rows and raise a clear ValueError instead of producing a
cryptic SQLAlchemy error at query time.
"""
pk_cols = inspect(column.property.mapper).primary_key
if len(pk_cols) != 1:
# Composite PKs would need a tuple `.in_()` and per-operator
# tuple handling; no Superset model uses one today, so we
# fail loudly rather than silently drop the trailing columns.
raise ValueError(
f"Relationship filter on '{col_name}' requires a "
f"single-column primary key on the related model; "
f"found {len(pk_cols)} columns."
)
related_pk = pk_cols[0]
if operator_enum in (ColumnOperatorEnum.eq, ColumnOperatorEnum.ne):
# `value` must be scalar for both eq and ne: a list/tuple would
# silently compile to `related_pk == [...]` (or `!= [...]`),
# which behaves unpredictably across backends instead of
# failing fast. Use `in`/`nin` to match multiple related ids.
if isinstance(value, (list, tuple)):
counterpart = "in" if operator_enum == ColumnOperatorEnum.eq else "nin"
raise ValueError(
f"Operator '{operator_enum.value}' on relationship "
f"column '{col_name}' requires a scalar value, got "
f"{type(value).__name__}. Use '{counterpart}' to match "
f"multiple related ids."
)View on GitHub (pinned to f4587218dd)
Solutions
- Filter on a scalar column instead of the collection relationship (e.g. the FK column on this model).
- Give the related model a single-column primary key (all stock Superset models have one) — or query the association table directly.
- If you control the code path, build the filter manually with tuple `.in_()` handling for composite keys.
Example fix
# before
filters=[{"col": "composite_pk_children", "opr": "eq", "value": 5}]
# after
filters=[{"col": "parent_id", "opr": "eq", "value": 5}] Defensive patterns
Strategy: validation
Validate before calling
from sqlalchemy.orm import RelationshipProperty, inspect
def relationship_has_single_pk(model_cls, rel_name: str) -> bool:
col = getattr(model_cls, rel_name, None)
if col is None or not hasattr(col, "property"):
return False
if not isinstance(col.property, RelationshipProperty) or not col.property.uselist:
return True # scalar path unaffected
return len(inspect(col.property.mapper).primary_key) == 1 Try / catch
try:
dao.find(query=", filters=filters)
except ValueError as ex:
if 'single-column primary key' in str(ex):
# rewrite filter against a scalar FK column on this model
... Prevention
- Prefer filtering on the local FK column over collection relationships.
- Keep custom association models on single-column PKs when they'll be filtered via DAOs.
- Test filter payloads against all target models in a matrix, not just one.
When it happens
Trigger: Passing filters like {"col": "<relationship_name>", "opr": "eq", "value": <id>} to any DAO where the relationship target model defines a composite primary key (len(inspect(mapper).primary_key) != 1).
Common situations: Custom Superset forks/plugins that add association models with composite PKs and try to filter them through the generic DAO filter machinery; MCP-driven dynamic filtering that assumes single-column PKs on every related model.
Related errors
- Invalid filter: column '%s' does not exist on %s
- Operator '{operator_enum.value}' on relationship column '{co
- Operator '{operator_enum.value}' is not supported on relatio
- created_by_fk_or_editor only supports 'eq'; got '{c.opr}'
- created_by_fk_or_editor only supports 'eq'; got '{c.opr}'
AI-assisted analysis of apache/superset@f4587218dd (2026-08-14).
Data as JSON: /api/errors/187199f168a2c907.
Report an issue: GitHub.