cube-js/cube · error · UserError
Incremental refresh key can only be used for partitioned pre
Error message
Incremental refresh key can only be used for partitioned pre-aggregations but set for non-partitioned '${cube}.${preAggregationName}' What it means
`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.
Source
Thrown at packages/cubejs-schema-compiler/src/adapter/BaseQuery.js:5120
if (preAggregation.refreshKey.sql) {
return [
preAggregationQueryForSql.paramAllocator.buildSqlAndParams(
preAggregationQueryForSql.evaluateSql(cube, preAggregation.refreshKey.sql)
).concat({
external: false,
renewalThreshold: preAggregation.refreshKey.every
? this.refreshKeyRenewalThresholdForInterval(preAggregation.refreshKey, false)
: this.defaultRefreshKeyRenewalThreshold(),
})
];
}
// eslint-disable-next-line prefer-const
let [refreshKey, refreshKeyExternal, refreshKeyQuery] = this.everyRefreshKeySql(preAggregation.refreshKey);
const renewalThreshold = this.refreshKeyRenewalThresholdForInterval(preAggregation.refreshKey);
if (preAggregation.refreshKey.incremental) {
if (!preAggregation.partitionGranularity) {
throw new UserError(`Incremental refresh key can only be used for partitioned pre-aggregations but set for non-partitioned '${cube}.${preAggregationName}'`);
}
// TODO Case when partitioned originalSql is resolved for query without time dimension.
// Consider fallback to not using such originalSql for consistency?
if (
preAggregationQueryForSql.timeDimensions.length &&
preAggregationQueryForSql.timeDimensions[0].dateRange
) {
refreshKey = this.incrementalRefreshKey(
preAggregationQueryForSql,
refreshKey,
{ window: preAggregation.refreshKey.updateWindow, refreshKeyQuery }
);
}
}
if (preAggregation.refreshKey.every || preAggregation.refreshKey.incremental) {
// An incremental key is wrapped into `CASE WHEN NOW() < <dateTo + updateWindow>`
// against an allocated partition range param, so it is not reproducible fromView on GitHub (pinned to 7d981676b3)
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
Example fix
// before
main: {
measureReferences: [orders.count],
refreshKey: { incremental: true, updateWindow: '1 day' }
}
// after (option A: partition it)
main: {
measureReferences: [orders.count],
timeDimensionReference: orders.createdAt,
partitionGranularity: 'month',
refreshKey: { incremental: true, updateWindow: '1 day' }
} Defensive patterns
Strategy: validation
Validate before calling
function validateIncremental(pa) {
if (pa.refreshKey?.incremental && !pa.partitionGranularity)
throw new Error('incremental refreshKey requires partitionGranularity');
} Type guard
const isIncrementalPartitioned = (pa) => Boolean(pa?.refreshKey?.incremental) && Boolean(pa?.partitionGranularity);
Try / catch
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; } Prevention
- 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
When it happens
Trigger: Defining a rollup without timeDimensionReference/partitionGranularity but with refreshKey: { incremental: true, updateWindow: '1 day' }.
Common situations: 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.
Related errors
- Unable to detect column types for pre-aggregation on empty v
- Create table failed: ${e}
- Unsupported table data passed to ${this.constructor}
- Unable to import (as rows) in Cube Store: empty columns. Mos
- Unsupported export bucket type: ${this.config.bucketType}
AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02).
Data as JSON: /api/errors/440c73474c3145c9.
Report an issue: GitHub.