{"record":{"id":"cfb483381445a0d7","repo":"cube-js/cube","slug":"can-t-find-common-parent-for-granularitya-and","errorCode":null,"errorMessage":"Can't find common parent for '${granularityA}' and '${granularityB}'","messagePattern":"Can't find common parent for '(.+?)' and '(.+?)'","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cubejs-schema-compiler/src/adapter/BaseQuery.js","lineNumber":1973,"sourceCode":"      return granularityB;\n    }\n    if (!granularityB) {\n      return granularityA;\n    }\n    if (granularityA === granularityB) {\n      return granularityA;\n    }\n    const aHierarchy = R.reverse(this.granularityParentHierarchy(granularityA));\n    const bHierarchy = R.reverse(this.granularityParentHierarchy(granularityB));\n    let lastIndex = Math.max(\n      aHierarchy.findIndex((g, i) => g !== bHierarchy[i]),\n      bHierarchy.findIndex((g, i) => g !== aHierarchy[i])\n    );\n    if (lastIndex === -1 && aHierarchy.length === bHierarchy.length) {\n      lastIndex = aHierarchy.length - 1;\n    }\n    if (lastIndex <= 0) {\n      throw new Error(`Can't find common parent for '${granularityA}' and '${granularityB}'`);\n    }\n    return aHierarchy[lastIndex - 1];\n  }\n\n  overTimeSeriesQuery(baseQueryFn, cumulativeMeasure, fromRollup) {\n    const dateJoinCondition = cumulativeMeasure.dateJoinCondition();\n    const uniqDateJoinCondition = R.uniqBy(djc => djc[0].dimension, dateJoinCondition);\n    const cumulativeMeasures = [cumulativeMeasure];\n    if (!this.timeDimensions.find(d => d.granularity)) {\n      const filters = this.segments\n        .concat(this.filters)\n        .concat(this.dateFromStartToEndConditionSql(\n          // If the same time dimension is passed more than once, no need to build the same\n          // filter condition again and again. Different granularities don't play role here,\n          // as rollingWindow.granularity is used for filtering.\n          uniqDateJoinCondition,\n          fromRollup,\n          false","sourceCodeStart":1955,"sourceCodeEnd":1991,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js#L1955-L1991","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use standard Cube granularities (second, minute, hour, day, week, month, quarter, year) so hierarchies share a common parent.","Fix misspelled granularity values in the query's timeDimensions.","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.","Wrap query building in UserError handling and surface a validation message about granularity compatibility."],"exampleFix":"// before\ntimeDimensions: [\n  { dimension: 'Events.time', dateRange: ['2024-01-01','2024-03-01'], granularity: 'day' },\n  { dimension: 'Orders.createdAt', dateRange: ['2024-01-01','2024-03-01'], granularity: 'fortnight' }\n]\n// after\ntimeDimensions: [\n  { dimension: 'Events.time', dateRange: ['2024-01-01','2024-03-01'], granularity: 'day' },\n  { dimension: 'Orders.createdAt', dateRange: ['2024-01-01','2024-03-01'], granularity: 'month' }\n]","handlingStrategy":"validation","validationCode":"const GRANS = ['second','minute','hour','day','week','month','quarter','year'];\nfunction validateGranularities(timeDimensions) {\n  const gs = timeDimensions.filter(td => td.granularity).map(td => td.granularity);\n  const bad = gs.filter(g => !GRANS.includes(g));\n  if (bad.length) throw new Error(`Invalid granularities: ${bad.join(', ')}`);\n  if (new Set(gs).size > 1) {\n    // ensure one is an ancestor of the other\n    const sorted = gs.map(g => GRANS.indexOf(g)).sort((a,b)=>a-b);\n    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');\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await cubeApi.load(query);\n} catch (e) {\n  if (/Can't find common parent for/.test(e.message)) {\n    console.error('Use standard granularities so a common parent exists');\n  }\n  throw e;\n}","preventionTips":["Only use standard Cube granularities in queries","Validate timeDimensions granularity values against the allowed enum client-side","Avoid mixing many different granularities in a single multi-timeDimension query"],"tags":["granularity","time-dimensions","query-validation"],"backgroundTag":"no-common-granularity-parent","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}