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

  1. Verify each cube in the rollupJoin has correct joins defined to the other cubes (both directions implied).
  2. Ensure the pre-aggregations listed in the rollupJoin actually span the joined cubes with full dimension paths (Cube.Dim.member).
  3. Remove pre-aggregations that only cover a single cube from the rollupJoin definition.
  4. 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

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


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