{"record":{"id":"24efa6fc7d1fbd25","repo":"cube-js/cube","slug":"data-blending-query-without-granularity-is-not-sup","errorCode":null,"errorMessage":"Data blending query without granularity is not supported","messagePattern":"Data blending query without granularity is not supported","errorType":"validation","errorClass":"UserError","httpStatus":null,"severity":"error","filePath":"packages/cubejs-api-gateway/src/gateway.ts","lineNumber":1516,"sourceCode":"      normalizedQueries: normalizedQueries.map(q => this.sanitizeQueryForLogging(q)),\n      duration: Date.now() - startTime,\n      query\n    }, context);\n\n    normalizedQueries = normalizedQueries.map(q => remapToQueryAdapterFormat(q));\n\n    if (normalizedQueries.some((currentQuery) => !currentQuery)) {\n      throw new Error('queryTransformer returned null query. Please check your queryTransformer implementation');\n    }\n\n    if (queryType === QueryTypeEnum.BLENDING_QUERY) {\n      const queryGranularity = getQueryGranularity(normalizedQueries);\n\n      if (queryGranularity.length > 1) {\n        throw new UserError('Data blending query granularities must match');\n      }\n      if (queryGranularity.length === 0) {\n        throw new UserError('Data blending query without granularity is not supported');\n      }\n    }\n\n    return [queryType, normalizedQueries, queryNormalizationResult.map((it) => remapToQueryAdapterFormat(it.normalizedQuery))];\n  }\n\n  protected async sql4sql({\n    query,\n    disablePostProcessing,\n    context,\n    res,\n  }: {query: string, disablePostProcessing: boolean} & BaseRequest) {\n    try {\n      await this.assertApiScope('sql', context.securityContext);\n\n      const result = await this.sqlServer.sql4sql(query, disablePostProcessing, context.securityContext);\n\n      await res({ sql: result });","sourceCodeStart":1498,"sourceCodeEnd":1534,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-api-gateway/src/gateway.ts#L1498-L1534","documentation":"A data blending query must include at least one time granularity so the sub-queries have a common axis to blend on. When getQueryGranularity returns an empty list — meaning none of the blended sub-queries has a granularity on its timeDimensions — the gateway throws this UserError instead of producing an ambiguous blend.","triggerScenarios":"POST /cubejs-api/v1/load with an array query where every sub-query's timeDimensions omit `granularity` (or no timeDimensions at all), while queryType resolves to BLENDING_QUERY.","commonSituations":"Users deselecting the date grouping in a dashboard builder while still requesting multiple datasets as a blend; constructing blended queries from raw JSON without a granularity field; assuming blending works with only filters/dateRanges.","solutions":["Add `granularity` (e.g. 'day' or 'month') to the timeDimension of each sub-query in the blended array.","Ensure the blend actually needs blending: if no time axis is required, send a single query with combined measures instead.","In dashboards, require the date-granularity picker to have a selection before issuing a blend request."],"exampleFix":"// before\n[{ measures:['a.c'], timeDimensions:[{dimension:'a.date', dateRange:['2024-01-01','2024-01-31']}] },\n { measures:['b.c'], timeDimensions:[{dimension:'b.date', dateRange:['2024-01-01','2024-01-31']}] }]\n// after\n[{ measures:['a.c'], timeDimensions:[{dimension:'a.date', granularity:'day', dateRange:['2024-01-01','2024-01-31']}] },\n { measures:['b.c'], timeDimensions:[{dimension:'b.date', granularity:'day', dateRange:['2024-01-01','2024-01-31']}] }]","handlingStrategy":"validation","validationCode":"function blendHasGranularity(queries) {\n  return queries.some(q => (q.timeDimensions ?? []).some(td => td.granularity));\n}\nif (Array.isArray(query) && !blendHasGranularity(query)) throw new Error('Blended queries require a time granularity');","typeGuard":"function hasAnyGranularity(qs: any[]): boolean {\n  return qs.some(q => (q.timeDimensions ?? []).some(td => typeof td.granularity === 'string' && td.granularity.length > 0));\n}","tryCatchPattern":"try {\n  return await cubeApi.load(queries);\n} catch (e) {\n  if (String(e?.message).includes('without granularity')) {\n    return await cubeApi.load(queries.map(q => ({ ...q, timeDimensions: withDefaultGranularity(q.timeDimensions) })));\n  }\n  throw e;\n}","preventionTips":["Always attach a default granularity (e.g. 'day') when building blended queries.","Disable blend submission in UIs until a date granularity is selected.","If no time axis is needed, collapse the blend into a single query."],"tags":["data-blending","query-validation","granularity"],"backgroundTag":"data-blending-missing-granularity","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}