cube-js/cube · error · UserError

Hierarchical time shift is not supported but was provided fo

Error message

Hierarchical time shift is not supported but was provided for '${memPath}'. Parent time shift is '${symbol.shiftInterval}' and current is '${this.safeEvaluateSymbolContext().timeShifts?.[memPath]}'

What it means

Cube propagates time shifts hierarchically when evaluating time symbols: a parent's shift is applied to child members. Applying another shift inside an already-shifted member is ambiguous/unsupported, so Cube throws this UserError when both a hierarchical shift (timeShifts[memPath] or commonTimeShift) and the symbol's own shiftInterval are present.

Source

Thrown at packages/cubejs-schema-compiler/src/adapter/BaseQuery.js:3541

          const td = this.newTimeDimension({
            dimension: this.cubeEvaluator.pathFromArray([cubeName, name]),
            granularity: subPropertyName
          });
          // for time dimension with granularity convertedToTz() is called internally in dimensionSql() flow,
          // so we need to ignore convertTz later even if context convertTzForRawTimeDimension is set to true
          return this.evaluateSymbolSqlWithContext(
            () => td.dimensionSql(),
            { ignoreConvertTzForTimeDimension: true },
          );
        } else {
          let res = this.autoPrefixAndEvaluateSql(cubeName, symbol.sql, isMemberExpr);
          const memPath = this.cubeEvaluator.pathFromArray([cubeName, name]);

          // Skip view's member evaluation as there will be underlying cube's same member evaluation
          if (symbol.type === 'time' && !this.cubeEvaluator.cubeFromPath(memPath).isView) {
            if (this.safeEvaluateSymbolContext().timeShifts?.[memPath]) {
              if (symbol.shiftInterval) {
                throw new UserError(`Hierarchical time shift is not supported but was provided for '${memPath}'. Parent time shift is '${symbol.shiftInterval}' and current is '${this.safeEvaluateSymbolContext().timeShifts?.[memPath]}'`);
              }
              res = `(${this.addTimestampInterval(res, this.safeEvaluateSymbolContext().timeShifts?.[memPath])})`;
            } else if (this.safeEvaluateSymbolContext().commonTimeShift) {
              if (symbol.shiftInterval) {
                throw new UserError(`Hierarchical time shift is not supported but was provided for '${memPath}'. Parent time shift is '${symbol.shiftInterval}' and current is '${this.safeEvaluateSymbolContext().commonTimeShift}'`);
              }
              res = `(${this.addTimestampInterval(res, this.safeEvaluateSymbolContext().commonTimeShift)})`;
            }
          }

          if (this.safeEvaluateSymbolContext().convertTzForRawTimeDimension &&
            !this.safeEvaluateSymbolContext().ignoreConvertTzForTimeDimension &&
            !memberExpressionType &&
            symbol.type === 'time' &&
            this.cubeEvaluator.byPathAnyType(memberPathArray).ownedByCube
          ) {
            res = this.convertTz(res);
          }

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Remove the shiftInterval from the member definition OR remove the query-level/commonTimeShift for that dimension — apply only one shift.
  2. Apply the combined shift manually: compute the net interval and pass a single timeShift.
  3. If the member lives in a view, ensure the shift is applied at one level only (view OR cube), not both.
  4. Catch UserError and surface guidance that hierarchical (stacked) time shifts are unsupported.

Example fix

// before (schema shift + query shift collide)
shiftInterval: '-1 month'  // in dimension definition
// and query: timeShifts: { 'Orders.createdAt': '-1 month' }
// after (single shift)
// remove schema shiftInterval, keep only:
query.timeShifts = { 'Orders.createdAt': '-2 month' } // combine intervals manually
Defensive patterns

Strategy: validation

Validate before calling

// Ensure a member is shifted at most once (schema OR query), never both
function assertSingleShift(dimensionDef, queryTimeShifts, memberPath) {
  const schemaShift = Boolean(dimensionDef && dimensionDef.shiftInterval);
  const queryShift = Boolean(queryTimeShifts && queryTimeShifts[memberPath]);
  if (schemaShift && queryShift) {
    throw new Error(`Member ${memberPath} is shifted both in schema and query; remove one`);
  }
}

Type guard

const hasNoStackedShift = (symbol, ctx, memPath) => !((ctx.timeShifts?.[memPath] || ctx.commonTimeShift) && symbol.shiftInterval);

Try / catch

try {
  return await cubeApi.load(query);
} catch (e) {
  if (/Hierarchical time shift is not supported/.test(e.message)) {
    console.error('Apply the time shift at one level only (schema or query)');
  }
  throw e;
}

Prevention

When it happens

Trigger: A time dimension (non-view) is evaluated with a timeShift in the evaluation context (or a commonTimeShift), and the same member's symbol also defines its own shiftInterval — e.g. shifting a shifted dimension, or a query-level timeShift applied to a dimension that already has a shiftInterval in its definition.

Common situations: Applying query-level timeShifts (dateRange shift API) on top of schema-defined shifted dimensions; nested shifted views; rolling window definitions combined with per-query time shift parameters.

Related errors


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