Skip to content

Timeseries Bar: stacked 'Only Total' sum includes the series-limit/sort metric's value, not just the displayed metrics #42701

Closed

Description

@gkhnelbstn

Summary

On a stacked Timeseries Bar chart (echarts_timeseries_bar) with "Show Total"/"Only Total" enabled, the displayed stacked total is silently inflated when the chart also uses a timeseries_limit_metric (the "Sort by" metric used to order/limit the x-axis) that isn't one of the chart's displayed metrics. The sort metric's value gets summed into the total even though it's correctly excluded from the actual rendered series/legend.

Reproduction

  1. Create an echarts_timeseries_bar chart with 2+ metrics (e.g. custom SQL metrics A, B), Stacked, "Only Total" enabled.
  2. Set Sort Series By / Series limit sort by (timeseries_limit_metric) to a metric that is not among the displayed metrics — e.g. MIN(some_sequence_column) labeled Sort, used purely so X-Axis Sort By can order categories by their natural sequence instead of alphabetically. The generated query becomes roughly:
    SELECT category, A, B, MIN(some_sequence_column) AS "Sort"
    FROM ...
    GROUP BY category
    ORDER BY "Sort"
  3. A real returned row looks like: {"category": "1-3g", "A": 32, "B": 0, "Sort": 2}.
  4. The chart correctly shows only 2 series in the legend (A, B) — Sort is excluded from the rendered series.
  5. Bug: the "Only Total" stacked label for this category shows 34 (32 + 0 + 2) instead of the correct 32 (32 + 0). The sort metric's value silently leaks into the total.

Root cause

plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts:

const { totalStackedValues, thresholdValues } = extractDataTotalValues(
  rebasedData,
  { stack, percentageThreshold, xAxisCol: xAxisLabel, legendState },
);
const extraMetricLabels = extractExtraMetrics(chartProps.rawFormData).map(getMetricLabel);

extraMetricLabels — which correctly excludes series-limit/sort-only metrics when building the actual chart series via extractSeries — is computed after extractDataTotalValues runs, and is never passed into it.

plugins/plugin-chart-echarts/src/utils/series.ts, extractDataTotalValues:

export function extractDataTotalValues(
  data: DataRecord[],
  opts: { stack: StackType; percentageThreshold: number; xAxisCol: string; legendState?: LegendState },
): { totalStackedValues: number[]; thresholdValues: number[] } {
  ...
  data.forEach(datum => {
    const values = Object.keys(datum).reduce((prev, curr) => {
      if (curr === xAxisCol) return prev;
      if (legendState && !legendState[curr]) return prev;
      const value = datum[curr] || 0;
      return prev + (value as number);
    }, 0);
    ...
  });

It sums every remaining numeric key in each row. The only exclusions are the x-axis column and legend-toggled-off series — there's no way to exclude a metric that was added to the query purely for sorting/limiting (via extractExtraMetrics in buildQuery.ts, comment: // only add series limit metric if it's explicitly needed e.g. for sorting). Since that column is never a visible legend entry, legendState can never exclude it either.

Suggested fix

Thread extraMetricLabels into extractDataTotalValues and exclude them the same way xAxisCol is excluded:

export function extractDataTotalValues(
  data: DataRecord[],
  opts: {
    stack: StackType;
    percentageThreshold: number;
    xAxisCol: string;
    legendState?: LegendState;
    extraMetricLabels?: string[]; // NEW
  },
): { totalStackedValues: number[]; thresholdValues: number[] } {
  const { stack, percentageThreshold, xAxisCol, legendState, extraMetricLabels } = opts;
  const excludedKeys = new Set([xAxisCol, ...(extraMetricLabels ?? [])]);
  ...
  data.forEach(datum => {
    const values = Object.keys(datum).reduce((prev, curr) => {
      if (excludedKeys.has(curr)) return prev;
      if (legendState && !legendState[curr]) return prev;
      ...

And in transformProps.ts, move the extraMetricLabels computation above the extractDataTotalValues call and pass it through via opts.

Impact

Any stacked Timeseries Bar/Area chart combining "Only Total"/"Show Total" with a timeseries_limit_metric that differs from the displayed metrics will show an inflated, incorrect stacked total. This is easy to hit in practice: timeseries_limit_metric is the standard way to control x-axis category ordering when the natural order isn't alphabetical (e.g. binned/bucketed labels like 1-3d, 4-6d, 7-9d, 10-12d, ...), which is precisely the situation where the sort metric is likely to differ from the displayed metrics.

Workaround

Avoid combining a timeseries_limit_metric that isn't a displayed metric with "Only Total"/"Show Total" on a stacked chart. If category ordering is the only reason for the extra metric, consider instead encoding the sort order directly into the x-axis column's values (e.g. zero-padded numeric prefixes) so X-Axis Sort By can use simple name/alphabetical ordering without needing a separate sort metric at all.

Environment

  • Superset version: 6.1.0 (confirmed present at refs/tags/6.1.0)
  • Chart type: echarts_timeseries_bar (Time-series Bar Chart), stacked, "Only Total" enabled

Activity

  1. rusackas commented on Aug 4, 2026

    @rusackas
    Member

    Looks like you already have a fix proposed... would you want to open a PR?

  2. rusackas commented on Aug 9, 2026

    @rusackas
    Member

    OMG, four PRs opened? That has to be a first. I'll see if I can pick a winner.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions