cube-js/cube · error · Error

Can't find common parent for '${granularityA}' and '${granul

Error message

Can't find common parent for '${granularityA}' and '${granularityB}'

What it means

Thrown by the granularity-hierarchy helper that computes the common parent granularity of two granularities. It walks both granularity hierarchies to the deepest shared index; if there is no shared prefix (lastIndex <= 0 and hierarchies differ), no common parent exists and it throws.

Source

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

      return granularityB;
    }
    if (!granularityB) {
      return granularityA;
    }
    if (granularityA === granularityB) {
      return granularityA;
    }
    const aHierarchy = R.reverse(this.granularityParentHierarchy(granularityA));
    const bHierarchy = R.reverse(this.granularityParentHierarchy(granularityB));
    let lastIndex = Math.max(
      aHierarchy.findIndex((g, i) => g !== bHierarchy[i]),
      bHierarchy.findIndex((g, i) => g !== aHierarchy[i])
    );
    if (lastIndex === -1 && aHierarchy.length === bHierarchy.length) {
      lastIndex = aHierarchy.length - 1;
    }
    if (lastIndex <= 0) {
      throw new Error(`Can't find common parent for '${granularityA}' and '${granularityB}'`);
    }
    return aHierarchy[lastIndex - 1];
  }

  overTimeSeriesQuery(baseQueryFn, cumulativeMeasure, fromRollup) {
    const dateJoinCondition = cumulativeMeasure.dateJoinCondition();
    const uniqDateJoinCondition = R.uniqBy(djc => djc[0].dimension, dateJoinCondition);
    const cumulativeMeasures = [cumulativeMeasure];
    if (!this.timeDimensions.find(d => d.granularity)) {
      const filters = this.segments
        .concat(this.filters)
        .concat(this.dateFromStartToEndConditionSql(
          // If the same time dimension is passed more than once, no need to build the same
          // filter condition again and again. Different granularities don't play role here,
          // as rollingWindow.granularity is used for filtering.
          uniqDateJoinCondition,
          fromRollup,
          false

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Use standard Cube granularities (second, minute, hour, day, week, month, quarter, year) so hierarchies share a common parent.
  2. Fix misspelled granularity values in the query's timeDimensions.
  3. If multiple time dimensions are required, align them so one granularity is an ancestor of the other (e.g. day + month), or run separate queries.
  4. Wrap query building in UserError handling and surface a validation message about granularity compatibility.

Example fix

// before
timeDimensions: [
  { dimension: 'Events.time', dateRange: ['2024-01-01','2024-03-01'], granularity: 'day' },
  { dimension: 'Orders.createdAt', dateRange: ['2024-01-01','2024-03-01'], granularity: 'fortnight' }
]
// after
timeDimensions: [
  { dimension: 'Events.time', dateRange: ['2024-01-01','2024-03-01'], granularity: 'day' },
  { dimension: 'Orders.createdAt', dateRange: ['2024-01-01','2024-03-01'], granularity: 'month' }
]
Defensive patterns

Strategy: validation

Validate before calling

const GRANS = ['second','minute','hour','day','week','month','quarter','year'];
function validateGranularities(timeDimensions) {
  const gs = timeDimensions.filter(td => td.granularity).map(td => td.granularity);
  const bad = gs.filter(g => !GRANS.includes(g));
  if (bad.length) throw new Error(`Invalid granularities: ${bad.join(', ')}`);
  if (new Set(gs).size > 1) {
    // ensure one is an ancestor of the other
    const sorted = gs.map(g => GRANS.indexOf(g)).sort((a,b)=>a-b);
    if (GRANS[sorted[0]] !== 'day' && GRANS[sorted[0]] !== 'second' && GRANS[sorted[0]] !== 'minute' && GRANS[sorted[0]] !== 'hour' && GRANS[sorted[0]] !== 'week' && GRANS[sorted[0]] !== 'month' && GRANS[sorted[0]] !== 'quarter' && GRANS[sorted[0]] !== 'year') throw new Error('no common parent');
  }
}

Try / catch

try {
  await cubeApi.load(query);
} catch (e) {
  if (/Can't find common parent for/.test(e.message)) {
    console.error('Use standard granularities so a common parent exists');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling overTimeSeriesQuery / minGranularity paths where two time dimensions (or a cumulative measure and a time dimension) have granularities that share no common ancestor — e.g. mixing granularities from different hierarchies or an unknown/invalid granularity value.

Common situations: Passing 'quarter'-style custom or misspelled granularities (e.g. 'week' typo, 'hours' instead of 'hour') in a query with multiple time dimensions, or combining granularities from different cubes whose hierarchies don't overlap.

Related errors


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