cube-js/cube · error · UserError

Data blending query without granularity is not supported

Error message

Data blending query without granularity is not supported

What it means

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.

Source

Thrown at packages/cubejs-api-gateway/src/gateway.ts:1516

      normalizedQueries: normalizedQueries.map(q => this.sanitizeQueryForLogging(q)),
      duration: Date.now() - startTime,
      query
    }, context);

    normalizedQueries = normalizedQueries.map(q => remapToQueryAdapterFormat(q));

    if (normalizedQueries.some((currentQuery) => !currentQuery)) {
      throw new Error('queryTransformer returned null query. Please check your queryTransformer implementation');
    }

    if (queryType === QueryTypeEnum.BLENDING_QUERY) {
      const queryGranularity = getQueryGranularity(normalizedQueries);

      if (queryGranularity.length > 1) {
        throw new UserError('Data blending query granularities must match');
      }
      if (queryGranularity.length === 0) {
        throw new UserError('Data blending query without granularity is not supported');
      }
    }

    return [queryType, normalizedQueries, queryNormalizationResult.map((it) => remapToQueryAdapterFormat(it.normalizedQuery))];
  }

  protected async sql4sql({
    query,
    disablePostProcessing,
    context,
    res,
  }: {query: string, disablePostProcessing: boolean} & BaseRequest) {
    try {
      await this.assertApiScope('sql', context.securityContext);

      const result = await this.sqlServer.sql4sql(query, disablePostProcessing, context.securityContext);

      await res({ sql: result });

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Add `granularity` (e.g. 'day' or 'month') to the timeDimension of each sub-query in the blended array.
  2. Ensure the blend actually needs blending: if no time axis is required, send a single query with combined measures instead.
  3. In dashboards, require the date-granularity picker to have a selection before issuing a blend request.

Example fix

// before
[{ measures:['a.c'], timeDimensions:[{dimension:'a.date', dateRange:['2024-01-01','2024-01-31']}] },
 { measures:['b.c'], timeDimensions:[{dimension:'b.date', dateRange:['2024-01-01','2024-01-31']}] }]
// after
[{ measures:['a.c'], timeDimensions:[{dimension:'a.date', granularity:'day', dateRange:['2024-01-01','2024-01-31']}] },
 { measures:['b.c'], timeDimensions:[{dimension:'b.date', granularity:'day', dateRange:['2024-01-01','2024-01-31']}] }]
Defensive patterns

Strategy: validation

Validate before calling

function blendHasGranularity(queries) {
  return queries.some(q => (q.timeDimensions ?? []).some(td => td.granularity));
}
if (Array.isArray(query) && !blendHasGranularity(query)) throw new Error('Blended queries require a time granularity');

Type guard

function hasAnyGranularity(qs: any[]): boolean {
  return qs.some(q => (q.timeDimensions ?? []).some(td => typeof td.granularity === 'string' && td.granularity.length > 0));
}

Try / catch

try {
  return await cubeApi.load(queries);
} catch (e) {
  if (String(e?.message).includes('without granularity')) {
    return await cubeApi.load(queries.map(q => ({ ...q, timeDimensions: withDefaultGranularity(q.timeDimensions) })));
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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