{"record":{"id":"440c73474c3145c9","repo":"cube-js/cube","slug":"incremental-refresh-key-can-only-be-used-for-parti","errorCode":null,"errorMessage":"Incremental refresh key can only be used for partitioned pre-aggregations but set for non-partitioned '${cube}.${preAggregationName}'","messagePattern":"Incremental refresh key can only be used for partitioned pre-aggregations but set for non-partitioned '(.+?)\\.(.+?)'","errorType":"validation","errorClass":"UserError","httpStatus":null,"severity":"error","filePath":"packages/cubejs-schema-compiler/src/adapter/BaseQuery.js","lineNumber":5120,"sourceCode":"          if (preAggregation.refreshKey.sql) {\n            return [\n              preAggregationQueryForSql.paramAllocator.buildSqlAndParams(\n                preAggregationQueryForSql.evaluateSql(cube, preAggregation.refreshKey.sql)\n              ).concat({\n                external: false,\n                renewalThreshold: preAggregation.refreshKey.every\n                  ? this.refreshKeyRenewalThresholdForInterval(preAggregation.refreshKey, false)\n                  : this.defaultRefreshKeyRenewalThreshold(),\n              })\n            ];\n          }\n\n          // eslint-disable-next-line prefer-const\n          let [refreshKey, refreshKeyExternal, refreshKeyQuery] = this.everyRefreshKeySql(preAggregation.refreshKey);\n          const renewalThreshold = this.refreshKeyRenewalThresholdForInterval(preAggregation.refreshKey);\n          if (preAggregation.refreshKey.incremental) {\n            if (!preAggregation.partitionGranularity) {\n              throw new UserError(`Incremental refresh key can only be used for partitioned pre-aggregations but set for non-partitioned '${cube}.${preAggregationName}'`);\n            }\n            // TODO Case when partitioned originalSql is resolved for query without time dimension.\n            // Consider fallback to not using such originalSql for consistency?\n            if (\n              preAggregationQueryForSql.timeDimensions.length &&\n              preAggregationQueryForSql.timeDimensions[0].dateRange\n            ) {\n              refreshKey = this.incrementalRefreshKey(\n                preAggregationQueryForSql,\n                refreshKey,\n                { window: preAggregation.refreshKey.updateWindow, refreshKeyQuery }\n              );\n            }\n          }\n\n          if (preAggregation.refreshKey.every || preAggregation.refreshKey.incremental) {\n            // An incremental key is wrapped into `CASE WHEN NOW() < <dateTo + updateWindow>`\n            // against an allocated partition range param, so it is not reproducible from","sourceCodeStart":5102,"sourceCodeEnd":5138,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js#L5102-L5138","documentation":"`refreshKey: { incremental: true }` builds the refresh key from partition boundaries, so it requires the pre-aggregation to declare partitionGranularity. If incremental is set on a non-partitioned (plain rollup) pre-aggregation, this UserError is thrown during pre-aggregation SQL build.","triggerScenarios":"Defining a rollup without timeDimensionReference/partitionGranularity but with refreshKey: { incremental: true, updateWindow: '1 day' }.","commonSituations":"Copying an incremental refresh example into an existing unpartitioned pre-aggregation; removing the time partitioning while keeping the refreshKey config; automated config generation that always sets incremental: true.","solutions":["Add partitionGranularity (and a timeDimensions partition reference) to the pre-aggregation, e.g. partitionGranularity: 'month'","Remove `incremental: true` from the refreshKey if the pre-aggregation should stay unpartitioned","Use a regular refreshKey (e.g. every: '1 hour') for non-partitioned rollups"],"exampleFix":"// before\nmain: {\n  measureReferences: [orders.count],\n  refreshKey: { incremental: true, updateWindow: '1 day' }\n}\n// after (option A: partition it)\nmain: {\n  measureReferences: [orders.count],\n  timeDimensionReference: orders.createdAt,\n  partitionGranularity: 'month',\n  refreshKey: { incremental: true, updateWindow: '1 day' }\n}","handlingStrategy":"validation","validationCode":"function validateIncremental(pa) {\n  if (pa.refreshKey?.incremental && !pa.partitionGranularity)\n    throw new Error('incremental refreshKey requires partitionGranularity');\n}","typeGuard":"const isIncrementalPartitioned = (pa) => Boolean(pa?.refreshKey?.incremental) && Boolean(pa?.partitionGranularity);","tryCatchPattern":"try { await cubeApi.query(q); } catch (e) { if (/Incremental refresh key can only be used for partitioned/.test(e.message)) console.error('Add partitionGranularity or drop incremental:', e.message); throw e; }","preventionTips":["Set incremental: true only on pre-aggregations with timeDimensionReference + partitionGranularity","When removing partitioning, also remove the incremental refreshKey","Add a model-level validation script for refreshKey configs"],"tags":["pre-aggregation","refresh-key","incremental-refresh"],"backgroundTag":"incremental-refresh-key-misconfigured","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}