cube-js/cube · error · UserError

unionWithSourceData can be enabled only for pre-aggregation

Error message

unionWithSourceData can be enabled only for pre-aggregation within '${preAggObj.cube}' cube but '${referencedPreAggregations[i].preAggregationName}' pre-aggregation is defined within '${referencedPreAggregations[i].cube}' cube

What it means

When the last rollup referenced by a rollupLambda has unionWithSourceData enabled, Cube requires it to live in the same cube as the lambda itself, because unioning source data is only valid within the lambda's own cube. Cross-cube usage throws this UserError.

Source

Thrown at packages/cubejs-schema-compiler/src/adapter/PreAggregations.ts:1213

    } else if (preAggregation.type === 'rollupLambda') {
      // TODO evaluation optimizations. Should be cached or moved to compile time.
      const referencedPreAggregations = preAggObj.references.rollups.map(
        name => {
          const [referencedCube, referencedPreAggregation] = this.query.cubeEvaluator.parsePath('preAggregations', name);
          return this.evaluatedPreAggregationObj(
            referencedCube,
            referencedPreAggregation,
            this.query.cubeEvaluator.byPath('preAggregations', name) as PreAggregationDefinitionExtended,
            canUsePreAggregation
          );
        }
      );
      if (referencedPreAggregations.length === 0) {
        throw new UserError(`rollupLambda '${cube}.${preAggregationName}' should reference at least one rollup`);
      }
      referencedPreAggregations.forEach((referencedPreAggregation, i) => {
        if (i === referencedPreAggregations.length - 1 && preAggObj.preAggregation.unionWithSourceData && preAggObj.cube !== referencedPreAggregations[i].cube) {
          throw new UserError(`unionWithSourceData can be enabled only for pre-aggregation within '${preAggObj.cube}' cube but '${referencedPreAggregations[i].preAggregationName}' pre-aggregation is defined within '${referencedPreAggregations[i].cube}' cube`);
        }
        referencedPreAggregations[i] = {
          ...referencedPreAggregations[i],
          preAggregation: {
            ...referencedPreAggregations[i].preAggregation,
            unionWithSourceData: i === referencedPreAggregations.length - 1 ? preAggObj.preAggregation.unionWithSourceData : false,
            rollupLambdaId: `${cube}.${preAggregationName}`,
            lastRollupLambda: i === referencedPreAggregations.length - 1,
            rollupLambdaTimeDimensionsReference: preAggObj.references.timeDimensions,
          }
        };
        if (i > 0) {
          const partitionGranularity = PreAggregations.checkPartitionGranularityDefined(cube, preAggregationName, referencedPreAggregations[i]);
          const prevReferencedPreAggregation = referencedPreAggregations[i - 1];
          const partitionGranularityPrev = PreAggregations.checkPartitionGranularityDefined(cube, preAggregationName, prevReferencedPreAggregation);
          const minGranularity = this.query.minGranularity(partitionGranularityPrev, partitionGranularity);
          if (minGranularity !== partitionGranularity) {
            throw new UserError(`'${prevReferencedPreAggregation.cube}.${prevReferencedPreAggregation.preAggregationName}' and '${referencedPreAggregation.cube}.${referencedPreAggregation.preAggregationName}' referenced by '${cube}.${preAggregationName}' rollupLambda have incompatible partition granularities. '${partitionGranularityPrev}' can't be padded by '${partitionGranularity}'`);

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Make the last rollup in the rollups array a pre-aggregation defined in the same cube as the rollupLambda.
  2. Set unionWithSourceData: false (or remove it) if cross-cube unioning is not actually needed.
  3. Move the rollupLambda definition into the cube that owns the referenced pre-aggregation.

Example fix

// before
// Cube A
monthlyUnion: { type: 'rollupLambda', unionWithSourceData: true, rollups: [B.ordersByMonth] }
// after
// Cube A
monthlyUnion: { type: 'rollupLambda', unionWithSourceData: true, rollups: [A.ordersByMonth] } // same cube
B_orders: { type: 'rollupLambda', rollups: [B.ordersByMonth] } // or lambda in B
Defensive patterns

Strategy: validation

Validate before calling

// unionWithSourceData requires the last rollup to live in the same cube as the lambda
function validateUnionWithSourceData(lambdaCube, def, rollupCubes) {
  if (def.unionWithSourceData && rollupCubes[rollupCubes.length - 1] !== lambdaCube) {
    throw new Error(`Last rollup (${rollupCubes[rollupCubes.length - 1]}) must be in cube ${lambdaCube} when unionWithSourceData is true`);
  }
}

Type guard

null

Try / catch

try { await cube.query(query); } catch (e) { if (/unionWithSourceData can be enabled only for pre-aggregation within/.test(e.message)) { console.error('Move the last rollup into the lambda cube or disable unionWithSourceData:', e.message); } else throw e; }

Prevention

When it happens

Trigger: A rollupLambda on cube A sets unionWithSourceData: true, and the final entry in its rollups array resolves to a pre-aggregation defined on cube B (preAggObj.cube !== referencedPreAggregations[last].cube).

Common situations: Configuring unionWithSourceData on a lambda whose last rollup comes from another cube's pre-aggregation; moving a rollup to another cube during refactoring while keeping unionWithSourceData; misunderstanding that the flag applies to the lambda's cube, not any referenced rollup.

Related errors


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