cube-js/cube · error · Error

Mixed offset rolling window querying is not supported

Error message

Mixed offset rolling window querying is not supported

What it means

When computing the maximum rolling window of two windows, if one window has offset 'start' (leading from start) and the other 'end' (or unset), no single combined window can represent both, so maxRollingWindow throws. The TODO notes a virtual 'both' offset would be needed.

Source

Thrown at packages/cubejs-schema-compiler/src/adapter/CubeStoreQuery.ts:295

      trailing = a.trailing;
    } else {
      trailing = this.parseSecondDuration(a.trailing) > this.parseSecondDuration(b.trailing) ? a.trailing : b.trailing;
    }

    let leading;
    if (a.leading === 'unbounded' || b.leading === 'unbounded') {
      leading = 'unbounded';
    } else if (!a.leading) {
      leading = b.leading;
    } else if (!b.leading) {
      leading = a.leading;
    } else {
      leading = this.parseSecondDuration(a.leading) > this.parseSecondDuration(b.leading) ? a.leading : b.leading;
    }

    if ((a.offset || 'end') !== (b.offset || 'end')) {
      // TODO introduce virtual 'both' offset and return it if max receives 'start' and 'end'
      throw new Error('Mixed offset rolling window querying is not supported');
    }

    return {
      trailing,
      leading,
      offset: a.offset
    };
  }

  public overTimeSeriesForSelectRollup(cumulativeMeasures, otherMeasures, timeDimension, preAggregationForQuery) {
    const rollupMeasures = this.preAggregations.rollupMeasures(preAggregationForQuery);
    const renderedReference = rollupMeasures.map(measure => {
      const m = this.newMeasure(measure);
      const renderSql = () => {
        if (timeDimension && m.isCumulative()) {
          const measureSql = m.cumulativeMeasureSql();
          const rollingWindow = m.rollingWindowDefinition();
          const preceding = rollingWindow.trailing ? `${this.toInterval(rollingWindow.trailing)} PRECEDING` : '';

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Make all rolling windows in the query use the same offset (all 'start' or all 'end').
  2. Split into two separate queries, one per offset type.
  3. Remove the explicit offset from one window so both default to 'end'.

Example fix

// before
rollingWindow: { trailing: '2 day', offset: 'start' } // in one measure
rollingWindow: { trailing: '7 day' } // in another -> defaults to 'end'
// after
rollingWindow: { trailing: '2 day', offset: 'end' }
rollingWindow: { trailing: '7 day', offset: 'end' }
Defensive patterns

Strategy: validation

Validate before calling

const offsets = measures.map(m => m.rollingWindow?.offset || 'end');
if (new Set(offsets).size > 1) throw new Error('All rolling windows must share the same offset');

Try / catch

try { await cube.load(q) } catch (e) { if (/Mixed offset rolling window/.test(e.message)) { /* split query per offset */ } else throw e; }

Prevention

When it happens

Trigger: A query (or merged query with multiple rolling windows on the same cube) mixes windows like `{ trailing: '2 day', offset: 'start' }` and `{ trailing: '2 day', offset: 'end' }`.

Common situations: Comparing current-period-to-date (offset: 'start') with trailing complete windows (offset: 'end') in the same query; orchestrator merging two queried rolling windows into one pre-aggregation window.

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/d08dea9f9ce3365b. Report an issue: GitHub.