{"record":{"id":"bcf1796e8be75235","repo":"apache/superset","slug":"metric-metric-s-does-not-exist","errorCode":null,"errorMessage":"Metric '%(metric)s' does not exist","messagePattern":"Metric '(.+?)' does not exist","errorType":"validation","errorClass":"QueryObjectValidationError","httpStatus":400,"severity":"error","filePath":"superset/connectors/sqla/models.py","lineNumber":2090,"sourceCode":"    def _get_series_orderby(\n        self,\n        series_limit_metric: Metric,\n        metrics_by_name: dict[str, SqlMetric],\n        columns_by_name: dict[str, TableColumn],\n        template_processor: BaseTemplateProcessor | None = None,\n    ) -> Column:\n        if utils.is_adhoc_metric(series_limit_metric):\n            assert isinstance(series_limit_metric, dict)\n            ob = self.adhoc_metric_to_sqla(series_limit_metric, columns_by_name)\n        elif (\n            isinstance(series_limit_metric, str)\n            and series_limit_metric in metrics_by_name\n        ):\n            ob = metrics_by_name[series_limit_metric].get_sqla_col(\n                template_processor=template_processor\n            )\n        else:\n            raise QueryObjectValidationError(\n                _(\"Metric '%(metric)s' does not exist\", metric=series_limit_metric)\n            )\n        return ob\n\n    def _get_top_groups(\n        self,\n        df: pd.DataFrame,\n        dimensions: list[str],\n        groupby_exprs: dict[str, Any],\n        columns_by_name: dict[str, TableColumn],\n    ) -> ColumnElement:\n        groups = []\n        for _unused, row in df.iterrows():\n            group = []\n            for dimension in dimensions:\n                value = self._normalize_prequery_result_type(\n                    row,\n                    dimension,","sourceCodeStart":2072,"sourceCodeEnd":2108,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/connectors/sqla/models.py#L2072-L2108","documentation":"QueryObjectValidationError raised in get_series_orderby_expression (models.py:2090) when series_limit_metric is neither an adhoc metric dict (is_adhoc_metric), nor a string matching a saved metric name in metrics_by_name. The series limit metric controls which metric drives row-order truncation (series_limit); it must resolve to one of the dataset's known metrics.","triggerScenarios":"Chart payload with series_limit_metric set to a metric name that is not saved on the dataset, or a dict that does not satisfy the adhoc-metric shape (e.g. missing expressionType). Emitted from queries that use series limiting (timeseries with row_limit per series).","commonSituations":"Metric renamed/deleted on the dataset while chart formData keeps the old series_limit_metric; dashboards imported from other instances referencing missing metrics; hand-written payloads with typos in the metric name.","solutions":["Set series_limit_metric to a metric that exists on the dataset (exact name) or remove it to fall back to the default ordering metric.","If the metric was renamed, update the dataset or re-save the chart so the reference matches.","For adhoc form, ensure the dict passes is_adhoc_metric (has expressionType and required fields)."],"exampleFix":"// before\nformData.series_limit_metric = 'sum___sales' // saved metric is 'Sales'\n\n// after\nformData.series_limit_metric = 'Sales'","handlingStrategy":"validation","validationCode":"def validate_series_limit_metric(metric, metrics_by_name) -> None:\n    if metric is None:\n        return\n    if isinstance(metric, str) and metric not in metrics_by_name:\n        raise ValueError(f\"series_limit_metric {metric!r} is not a saved metric on the dataset\")\n    if isinstance(metric, dict) and metric.get(\"expressionType\") not in {\"SIMPLE\", \"SQL\"}:\n        raise ValueError(\"series_limit_metric dict must be a valid adhoc metric\")","typeGuard":"def resolves_to_known_metric(metric, metrics_by_name) -> bool:\n    if isinstance(metric, str):\n        return metric in metrics_by_name\n    if isinstance(metric, dict):\n        return metric.get(\"expressionType\") in {\"SIMPLE\", \"SQL\"}\n    return False","tryCatchPattern":"from superset.exceptions import QueryObjectValidationError\n\ntry:\n    ob = table.get_series_orderby_expression(metric, columns_by_name, metrics_by_name)\nexcept QueryObjectValidationError as ex:\n    if \"does not exist\" in str(ex):\n        metric = None  # fall back to default ordering\n        ob = table.get_series_orderby_expression(metric, columns_by_name, metrics_by_name)\n    else:\n        raise","preventionTips":["When renaming/deleting dataset metrics, search chart formData for stale references (series_limit_metric included).","Prefer adhoc metric dicts for series_limit_metric to avoid name coupling.","Validate metric names against the datasource before programmatic chart creation."],"tags":["metric","series-limit","validation","charts"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}