{"record":{"id":"26711c0e2bb4df2a","repo":"cube-js/cube","slug":"nothing-to-join-in-rollup-join-target-joins-jso","errorCode":null,"errorMessage":"Nothing to join in rollup join. Target joins ${JSON.stringify(targetJoins)} are included in existing rollup joins ${JSON.stringify(existingJoins)}","messagePattern":"Nothing to join in rollup join\\. Target joins (.+?) are included in existing rollup joins (.+?)","errorType":"validation","errorClass":"UserError","httpStatus":null,"severity":"error","filePath":"packages/cubejs-schema-compiler/src/adapter/PreAggregations.ts","lineNumber":1071,"sourceCode":"        }\n\n        const targetJoins = this.resolveJoinMembers(builtJoinTree);\n\n        // TODO join hints?\n        const existingJoins = preAggObjsToJoin\n          .map(p => this.resolveJoinMembers(\n            this.query.joinTreeForHints(this.cubesHintsFromPreAggregation(p), true)\n          ))\n          .flat();\n\n        const nonExistingJoins = targetJoins.filter(target => !existingJoins.find(\n          existing => existing.originalFrom === target.originalFrom &&\n            existing.originalTo === target.originalTo &&\n            R.equals(existing.fromMembers, target.fromMembers) &&\n            R.equals(existing.toMembers, target.toMembers)\n        ));\n        if (!nonExistingJoins.length) {\n          throw new UserError(`Nothing to join in rollup join. Target joins ${JSON.stringify(targetJoins)} are included in existing rollup joins ${JSON.stringify(existingJoins)}`);\n        }\n        return nonExistingJoins.map(join => {\n          const fromPreAggObj = this.preAggObjForJoin(preAggObjsToJoin, join.fromMembers, join, `${preAggObj.cube}.${preAggObj.preAggregationName}`);\n          const toPreAggObj = this.preAggObjForJoin(preAggObjsToJoin, join.toMembers, join, `${preAggObj.cube}.${preAggObj.preAggregationName}`);\n          return {\n            ...join,\n            fromPreAggObj,\n            toPreAggObj\n          };\n        });\n      }\n    );\n  }\n\n  private preAggObjForJoin(\n    preAggObjsToJoin: PreAggregationForQuery[],\n    joinMembers: string[],\n    join: JoinEdgeWithMembers,","sourceCodeStart":1053,"sourceCodeEnd":1089,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-schema-compiler/src/adapter/PreAggregations.ts#L1053-L1089","documentation":"During rollup-join construction, Cube computes which target joins still need new pre-aggregations by subtracting joins already covered by existing rollup pre-aggregations. If the difference is empty, there is nothing new to join — every target join duplicates an existing one — which usually indicates a misconfigured rollupJoin, so it throws.","triggerScenarios":"A rollupJoin whose participating pre-aggregations already cover all joins in the built join tree: every target join's originalFrom/originalTo and member sets match an existing rollup join's, leaving nonExistingJoins empty.","commonSituations":"Listing the same join's pre-aggs in two rollupJoins; a rollupJoin that duplicates what a single pre-aggregation already provides; copy-pasting rollupJoin definitions after the underlying single-cube pre-aggregation was already replaced by a full rollup.","solutions":["Remove the redundant rollupJoin and query the existing (full) pre-aggregation directly.","Add at least one new cube/membership to the rollupJoin so there is a genuinely new join to materialize.","Deduplicate pre-aggregation lists across rollupJoin definitions.","Refactor to a single pre-aggregation that spans all required cubes if joins are already covered."],"exampleFix":"// before\nrollupJoin: { preAggregations: [ordersMain, usersMain] } // duplicates joins already in ordersUsersFull rollup\n// after\n// query 'ordersUsersFull' pre-aggregation directly, or extend rollupJoin with a third cube (e.g. productsMain)","handlingStrategy":"validation","validationCode":"// dedupe pre-aggregations across rollupJoins before submitting schema\nconst seen = new Set();\nfor (const rj of rollupJoins) {\n  const key = [...rj.preAggregations].sort().join('+');\n  if (seen.has(key)) throw new Error(`Redundant rollupJoin covering same joins twice: ${key}`);\n  seen.add(key);\n}","typeGuard":null,"tryCatchPattern":"try { await cubeApi.load(query); } catch (e) { if (/Nothing to join in rollup join/.test(e.message)) {\n  // the rollupJoin is redundant; query the existing full pre-aggregation instead\n  return queryViaExistingRollup(); } throw e; }","preventionTips":["Don't define rollupJoins whose joins are already covered by a single spanning pre-aggregation","Deduplicate preAggregations lists across rollupJoin definitions","Replace rollupJoins with a full single pre-aggregation when volume allows","Review rollupJoin definitions after adding/removing pre-aggregations"],"tags":["rollup-join","redundant","pre-aggregation"],"backgroundTag":"rollup-join-nothing-to-join","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}