{"record":{"id":"b669d6737e342bbb","repo":"cube-js/cube","slug":"granularity-this-granularity-of-dimension-t","errorCode":null,"errorMessage":"Granularity \"${this.granularity}\" of dimension ${timeDimension.dimension} ${cause}","messagePattern":"Granularity \"(.+?)\" of dimension (.+?) (.+?)","errorType":"validation","errorClass":"UserError","httpStatus":null,"severity":"error","filePath":"packages/cubejs-schema-compiler/src/adapter/Granularity.ts","lineNumber":55,"sourceCode":"\n    if (this.predefinedGranularity) {\n      this.granularityInterval = `1 ${this.granularity}`;\n    } else {\n      const customGranularity = this.query.cacheValue(\n        ['customGranularity', timeDimension.dimension, this.granularity],\n        () => query.cubeEvaluator\n          .resolveGranularity([...query.cubeEvaluator.parsePath('dimensions', timeDimension.dimension), 'granularities', this.granularity])\n      );\n\n      if (!customGranularity) {\n        throw new UserError(`Granularity \"${timeDimension.granularity}\" does not exist in dimension ${timeDimension.dimension}`);\n      }\n\n      if (!customGranularity.interval) {\n        const cause = customGranularity.sql\n          ? 'is defined with \\'sql\\', which is only supported for predefined granularities'\n          : 'has no interval';\n        throw new UserError(`Granularity \"${this.granularity}\" of dimension ${timeDimension.dimension} ${cause}`);\n      }\n\n      this.granularityInterval = customGranularity.interval;\n\n      if (customGranularity.origin) {\n        this.origin = moment.tz(customGranularity.origin, query.timezone);\n      } else if (customGranularity.offset) {\n        // Needed because if interval is week-based, offset is expected to be relative to the start of a week\n        this.fixOriginForWeeksIfNeeded();\n        this.granularityOffset = customGranularity.offset;\n        this.origin = addInterval(this.origin, parseSqlInterval(customGranularity.offset));\n      } else {\n        this.fixOriginForWeeksIfNeeded();\n      }\n    }\n  }\n\n  private fixOriginForWeeksIfNeeded() {","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-schema-compiler/src/adapter/Granularity.ts#L37-L73","documentation":"After resolving a custom granularity, the constructor checks it has an `interval`. If instead it only has `sql` (predefined-granularity-only feature) or is missing interval entirely, it throws this UserError explaining the cause. Custom granularities must be interval-based.","triggerScenarios":"Defining a custom granularity entry in a dimension using `sql:` instead of `interval:`, or declaring a custom granularity object without either field, then querying with that granularity.","commonSituations":"Developers copying the shape of predefined granularities (which are sql-based) into a custom granularities block; forgetting the interval key; schema migration from older experimental syntax.","solutions":["Replace `sql:` with a valid `interval` (e.g. `interval: '15 minutes'`) in the custom granularity definition.","Add a missing `interval` property to the custom granularity.","Use a predefined granularity if you need sql-based behavior."],"exampleFix":"// before\ngranularities: { quarterHour: { sql: `${CUBE}.timestamp` } }\n// after\ngranularities: { quarterHour: { interval: '15 minutes' } }","handlingStrategy":"validation","validationCode":"for (const [name, g] of Object.entries(dim.granularities || {})) {\n  if (!g.interval) throw new Error(`Custom granularity \"${name}\" must define an interval`);\n}","typeGuard":null,"tryCatchPattern":"try { await cube.load(q) } catch (e) { if (/has no interval|only supported for predefined granularities/.test(e.message)) { /* fix schema: replace sql with interval */ } else throw e; }","preventionTips":["Always specify interval on custom granularities","Never copy sql-based predefined granularity shapes into granularities block","Compile schemas in CI to surface model errors early"],"tags":["granularity","data-model","custom-granularity","user-error"],"backgroundTag":"granularity-missing-interval","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}