apache/superset · error · QueryObjectValidationError

Columns missing in dataset: %(invalid_columns)s

Error message

Columns missing in dataset: %(invalid_columns)s

What it means

QueryContextProcessor.get_payload (cache-key path) verifies that every column in query_obj.columns plus every metric name exists in the datasource's column_names (DTTM_ALIAS excepted). Any unknown name collects into invalid_columns and raises QueryObjectValidationError('Columns missing in dataset: ...').

Source

Thrown at superset/common/query_context_processor.py:146

        ):
            cache.is_loaded = False

        cache_resolution_ns = max(0, time.perf_counter_ns() - cache_resolution_start_ns)

        data_acquisition_ns: int | None = None
        if query_obj and cache_key and not cache.is_loaded:
            data_acquisition_start_ns = time.perf_counter_ns()
            try:
                if invalid_columns := [
                    col
                    for col in get_column_names_from_columns(query_obj.columns)
                    + get_column_names_from_metrics(query_obj.metrics or [])
                    if (
                        col not in self._qc_datasource.column_names
                        and col != DTTM_ALIAS
                    )
                ]:
                    raise QueryObjectValidationError(
                        _(
                            "Columns missing in dataset: %(invalid_columns)s",
                            invalid_columns=invalid_columns,
                        )
                    )

                query_result = self.get_query_result(query_obj)
                annotation_data = self.get_annotation_data(query_obj)
            except QueryObjectValidationError as ex:
                cache.error_message = str(ex)
                cache.status = QueryStatus.FAILED
            finally:
                data_acquisition_ns = max(
                    0, time.perf_counter_ns() - data_acquisition_start_ns
                )

            if cache.status != QueryStatus.FAILED:
                cache.set_query_result(

View on GitHub (pinned to f4587218dd)

Solutions

  1. Sync the dataset: refresh columns in the dataset editor (or via API) so it matches the physical schema.
  2. Update the chart's controls/params to use current column names.
  3. If the column exists in the DB but not the dataset, add it to the dataset metadata.

Example fix

# before
{"columns": ["user_nm"], ...}  # dataset has user_name

# after
{"columns": ["user_name"], ...}  # match dataset column_names
Defensive patterns

Strategy: validation

Validate before calling

valid = set(datasource.column_names) | {"__timestamp"}
invalid = [c for c in requested_columns if c not in valid]
if invalid:
    raise ValueError(f"unknown columns: {invalid}")

Type guard

def columns_exist(ds, cols: list[str]) -> bool:
    names = set(ds.column_names)
    return all(c in names or c == "__timestamp" for c in cols)

Try / catch

except QueryObjectValidationError as e:
    if "Columns missing in dataset" in str(e):
        sync_dataset_columns(); resubmit()

Prevention

When it happens

Trigger: POSTing a chart data request whose columns/metrics reference fields not present on the dataset — renamed or dropped DB columns, a stale chart payload after dataset changes, or a query context built against a different dataset.

Common situations: Underlying table schema changed (column renamed/dropped) while charts still request the old names; chart JSON copied to a different dataset; caching layer bypassed so the validation runs against current schema.

Related errors


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