apache/superset · error · QueryObjectValidationError

Adhoc metric expressionType is invalid

Error message

Adhoc metric expressionType is invalid

What it means

QueryObjectValidationError raised in SqlaTable.adhoc_metric_to_sqla (models.py:1931) when the adhoc metric's expressionType is neither 'SIMPLE' nor 'SQL'. Superset dispatches metric construction on this discriminator; any other value (typo, null, legacy value) falls to the else branch. Note this raise is the one place without _() translation, but the semantics are identical.

Source

Thrown at superset/connectors/sqla/models.py:1931

                raise QueryObjectValidationError(
                    _("Adhoc metric SQL expression is invalid")
                )

            if not processed:
                try:
                    expression = self._process_select_expression(
                        expression=expression,
                        database_id=self.database_id,
                        engine=self.database.backend,
                        schema=self.schema,
                        template_processor=template_processor,
                    )
                except SupersetSecurityException as ex:
                    raise QueryObjectValidationError(ex.message) from ex

            sqla_metric = literal_column(expression)
        else:
            raise QueryObjectValidationError("Adhoc metric expressionType is invalid")

        return self.make_sqla_column_compatible(sqla_metric, label)

    def _render_adhoc_expression_for_metadata_lookup(
        self,
        sql_expression: str,
        template_processor: BaseTemplateProcessor | None,
    ) -> str:
        """Render Jinja in *sql_expression* so the result can be matched against
        column metadata.  Without this, a templated expression such as
        ``{{ filter_values('x')[0] }}`` is passed raw to ``get_column``, never
        matches, and falls back to ``literal_column`` — which breaks for virtual
        datasets because the rendered name isn't present in the FROM subquery."""
        if not template_processor:
            return sql_expression
        try:
            return template_processor.process_template(sql_expression)
        except SupersetSyntaxErrorException as ex:

View on GitHub (pinned to f4587218dd)

Solutions

  1. Set expressionType to exactly 'SIMPLE' or 'SQL' (see superset/common/utils/query_utils.py AdhocMetricExpressionType).
  2. For simple aggregation: provide column + aggregate; for SQL: provide sqlExpression.
  3. Re-create the metric through the Explore UI to get canonical JSON.

Example fix

// before
{ expressionType: 'simple', column: { column_name: 'sales' }, aggregate: 'SUM' }

// after
{ expressionType: 'SIMPLE', column: { column_name: 'sales' }, aggregate: 'SUM' }
Defensive patterns

Strategy: type-guard

Validate before calling

VALID_EXPRESSION_TYPES = {"SIMPLE", "SQL"}

def validate_metric_shape(metric: dict) -> None:
    et = metric.get("expressionType")
    if et not in VALID_EXPRESSION_TYPES:
        raise ValueError(f"expressionType must be one of {VALID_EXPRESSION_TYPES}, got {et!r}")

Type guard

from typing import TypedDict, Literal, NotRequired

class AdhocMetric(TypedDict):
    expressionType: Literal["SIMPLE", "SQL"]
    column: NotRequired[dict]
    aggregate: NotRequired[str]
    sqlExpression: NotRequired[str]

def is_adhoc_metric_typed(metric: object) -> bool:
    return (
        isinstance(metric, dict)
        and metric.get("expressionType") in {"SIMPLE", "SQL"}
    )

Try / catch

from superset.exceptions import QueryObjectValidationError

try:
    col = table.adhoc_metric_to_sqla(metric, columns_by_name)
except QueryObjectValidationError as ex:
    if "expressionType is invalid" in str(ex):
        metric = {**metric, "expressionType": metric.get("expressionType", "").upper()}
        col = table.adhoc_metric_to_sqla(metric, columns_by_name)
    else:
        raise

Prevention

When it happens

Trigger: Adhoc metric dict like {expressionType: 'simple' | 'Custom' | null | 'Api'}, or a payload from a different Superset version/plugin using an unsupported expressionType enum value.

Common situations: Case mismatch ('simple' vs 'SIMPLE') from hand-written payloads; dashboard/chart export-import between versions whose AdhocMetricExpressionType enum differs; custom chart plugins emitting their own metric shapes.

Related errors


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