cube-js/cube · error · UserError
Can't build join tree for pre-aggregation ${preAggObj.cube}.
Error message
Can't build join tree for pre-aggregation ${preAggObj.cube}.${preAggObj.preAggregationName} What it means
When building a rollup-join, Cube tries to construct the join tree among the cubes referenced by a pre-aggregation. joinTreeForHints returned null (no joins could be derived from the pre-aggregation's cube hints), so the rollup join cannot be materialized and this UserError names the offending pre-aggregation.
Source
Thrown at packages/cubejs-schema-compiler/src/adapter/PreAggregations.ts:1052
for (const j of refs.joinTree.joins) {
hints.push([j.from, j.to]);
}
return hints;
}
// TODO check multiplication factor didn't change
private buildRollupJoin(preAggObj: PreAggregationForQuery, preAggObjsToJoin: PreAggregationForQuery[]): RollupJoin {
return this.query.cacheValue(
['buildRollupJoin', JSON.stringify(preAggObj), JSON.stringify(preAggObjsToJoin)],
() => {
// It's not enough to call buildJoin() directly on cubesFromPreAggregation()
// because transitive joins won't be collected in that case.
const builtJoinTree = this.query.joinTreeForHints(this.cubesHintsFromPreAggregation(preAggObj), true);
if (!builtJoinTree) {
throw new UserError(`Can't build join tree for pre-aggregation ${preAggObj.cube}.${preAggObj.preAggregationName}`);
}
const targetJoins = this.resolveJoinMembers(builtJoinTree);
// TODO join hints?
const existingJoins = preAggObjsToJoin
.map(p => this.resolveJoinMembers(
this.query.joinTreeForHints(this.cubesHintsFromPreAggregation(p), true)
))
.flat();
const nonExistingJoins = targetJoins.filter(target => !existingJoins.find(
existing => existing.originalFrom === target.originalFrom &&
existing.originalTo === target.originalTo &&
R.equals(existing.fromMembers, target.fromMembers) &&
R.equals(existing.toMembers, target.toMembers)
));
if (!nonExistingJoins.length) {View on GitHub (pinned to 7d981676b3)
Solutions
- Verify each cube in the rollupJoin has correct joins defined to the other cubes (both directions implied).
- Ensure the pre-aggregations listed in the rollupJoin actually span the joined cubes with full dimension paths (Cube.Dim.member).
- Remove pre-aggregations that only cover a single cube from the rollupJoin definition.
- Check that cubesHintsFromPreAggregation produces entries for all participating cubes — fix member references if some are missing.
Example fix
// before
rollupJoin: { dimensions: [Orders.status, Users.country], preAggregations: [ordersMain] } // ordersMain covers only Orders, no join path
// after
preAggregations: [Orders.ordersByStatusUser, Users.usersByCountry] // each pre-agg spans its cube's join side
rollupJoin: { preAggregations: [Orders.ordersByStatusUser, Users.usersByCountry] } Defensive patterns
Strategy: validation
Validate before calling
// before defining a rollupJoin, verify each cube in the join has joins declared and each pre-agg spans >1 cube
for (const [cubeName, cube] of Object.entries(schema)) {
if (participatingCubes.includes(cubeName) && !cube.joins) {
throw new Error(`Cube ${cubeName} used in rollupJoin has no joins defined`);
}
} Try / catch
try { await cubeApi.load(query); } catch (e) { if (/Can't build join tree for pre-aggregation/.test(e.message)) {
const preAgg = e.message.match(/pre-aggregation ([\w.]+)/)?.[1];
throw new Error(`Fix cube joins / member paths for ${preAgg}`); } throw e; } Prevention
- Define joins on every cube participating in a rollupJoin
- Only list multi-cube pre-aggregations in rollupJoin definitions
- Use fully qualified member paths (Cube.dimension)
- Write a schema test that builds all rollupJoins at compile time
When it happens
Trigger: A pre-aggregation referenced by a rollupJoin spans cubes whose members do not require any join (single cube) or whose join hints cannot be resolved — joinTreeForHints(cubesHints, true) yields no tree during rollup-join construction.
Common situations: Rollup joins defined where the 'from'/'to' cubes are actually the same cube or unrelated cubes with no defined join path; missing or incorrect joins section in the cube definitions; referencing a pre-agg that only covers one cube in a rollupJoin.
Related errors
- Nothing to join in rollup join. Target joins ${JSON.stringif
- No rollups found that can be used for a rollup join from "${
- Multiple rollups found that can be used for rollup join ${JS
- Members ${invalidMembers.join(', ')} in join from '${j.origi
- From members are not found in [${memberPaths.map(m => m.join
AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02).
Data as JSON: /api/errors/50152554a63c3ce6.
Report an issue: GitHub.