cube-js/cube · error · UserError

Granularity "${timeDimension.granularity}" does not exist in

Error message

Granularity "${timeDimension.granularity}" does not exist in dimension ${timeDimension.dimension}

What it means

Granularity constructor resolves the requested time dimension granularity. If it is not a predefined granularity (second..year) and cannot be resolved as a custom granularity defined on the dimension, it throws this UserError naming the granularity and dimension.

Source

Thrown at packages/cubejs-schema-compiler/src/adapter/Granularity.ts:48

    private readonly query: BaseQuery,
    timeDimension: any
  ) {
    this.granularity = timeDimension.granularity;
    this.predefinedGranularity = isPredefinedGranularity(this.granularity);
    this.queryTimezone = query.timezone || 'UTC';
    this.origin = moment.tz(query.timezone).startOf('year'); // Defaults to current year start

    if (this.predefinedGranularity) {
      this.granularityInterval = `1 ${this.granularity}`;
    } else {
      const customGranularity = this.query.cacheValue(
        ['customGranularity', timeDimension.dimension, this.granularity],
        () => query.cubeEvaluator
          .resolveGranularity([...query.cubeEvaluator.parsePath('dimensions', timeDimension.dimension), 'granularities', this.granularity])
      );

      if (!customGranularity) {
        throw new UserError(`Granularity "${timeDimension.granularity}" does not exist in dimension ${timeDimension.dimension}`);
      }

      if (!customGranularity.interval) {
        const cause = customGranularity.sql
          ? 'is defined with \'sql\', which is only supported for predefined granularities'
          : 'has no interval';
        throw new UserError(`Granularity "${this.granularity}" of dimension ${timeDimension.dimension} ${cause}`);
      }

      this.granularityInterval = customGranularity.interval;

      if (customGranularity.origin) {
        this.origin = moment.tz(customGranularity.origin, query.timezone);
      } else if (customGranularity.offset) {
        // Needed because if interval is week-based, offset is expected to be relative to the start of a week
        this.fixOriginForWeeksIfNeeded();
        this.granularityOffset = customGranularity.offset;
        this.origin = addInterval(this.origin, parseSqlInterval(customGranularity.offset));

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Fix the granularity name in the query to a predefined one (second, minute, hour, day, week, month, quarter, year).
  2. Define the custom granularity on the dimension with an `interval` (e.g. `granularities: { hour4: { interval: '4 hours' } }`).
  3. Ensure the client version supports custom granularities and the dimension path is correct.

Example fix

// before (query)
timeDimensions: [{ dimension: 'Events.timestamp', granularity: 'hour4' }]
// after (data model)
dimensions: { timestamp: { type: 'time', sql: `timestamp`, granularities: { hour4: { interval: '4 hours' } } } }
Defensive patterns

Strategy: validation

Validate before calling

const predefined = ['second','minute','hour','day','week','month','quarter','year'];
if (!predefined.includes(granularity)) {
  const dim = dataModel.dimensions[dimName];
  if (!dim?.granularities?.[granularity]) throw new Error(`Granularity "${granularity}" not defined on ${dimName}`);
}

Try / catch

try { await cube.load(q) } catch (e) { if (/does not exist in dimension/.test(e.message)) { /* retry with 'day' or fix data model */ } else throw e; }

Prevention

When it happens

Trigger: A query (REST/GraphQL timeDimensions or SQL API) requests `granularity: 'hour4'` (or any custom name) where the referenced dimension has no matching `granularities` entry, or the granularity name is misspelled.

Common situations: Typos in granularity names ('mont' vs 'month'); clients requesting custom granularities before the data model defines them; case-sensitivity mismatches.

Related errors


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