{"record":{"id":"b684154bf49fd864","repo":"cube-js/cube","slug":"no-rollups-found-that-can-be-used-for-a-rollup-joi","errorCode":null,"errorMessage":"No rollups found that can be used for a rollup join from \"${join.from}\" (fromMembers: ${JSON.stringify(join.fromMembers)}) to \"${join.to}\" (toMembers: ${JSON.stringify(join.toMembers)}). Check the \"${rollupJoinPreAggName}\" pre-aggregation definition — you may have forgotten to specify the full dimension paths","messagePattern":"No rollups found that can be used for a rollup join from \"(.+?)\" \\(fromMembers: (.+?)\\) to \"(.+?)\" \\(toMembers: (.+?)\\)\\. Check the \"(.+?)\" pre-aggregation definition — you may have forgotten to specify the full dimension paths","errorType":"validation","errorClass":"UserError","httpStatus":null,"severity":"error","filePath":"packages/cubejs-schema-compiler/src/adapter/PreAggregations.ts","lineNumber":1099,"sourceCode":"        });\n      }\n    );\n  }\n\n  private preAggObjForJoin(\n    preAggObjsToJoin: PreAggregationForQuery[],\n    joinMembers: string[],\n    join: JoinEdgeWithMembers,\n    rollupJoinPreAggName: string,\n  ): PreAggregationForQuery {\n    const fromPreAggObj = preAggObjsToJoin\n      .filter(p => joinMembers.every(m => !!p.references.dimensions.find(d => m === d)));\n    if (!fromPreAggObj.length) {\n      const msg = `No rollups found that can be used for a rollup join from \"${\n        join.from}\" (fromMembers: ${JSON.stringify(join.fromMembers)}) to \"${join.to}\" (toMembers: ${\n        JSON.stringify(join.toMembers)}). Check the \"${\n        rollupJoinPreAggName}\" pre-aggregation definition — you may have forgotten to specify the full dimension paths`;\n      throw new UserError(msg);\n    }\n    if (fromPreAggObj.length > 1) {\n      throw new UserError(\n        `Multiple rollups found that can be used for rollup join ${JSON.stringify(join)}: ${fromPreAggObj.map(p => this.preAggregationId(p)).join(', ')}`,\n      );\n    }\n    return fromPreAggObj[0];\n  }\n\n  private resolveJoinMembers(join: FinishedJoinTree): JoinEdgeWithMembers[] {\n    const joinMap = new Set<string>();\n\n    return join.joins.map(j => {\n      joinMap.add(j.originalFrom);\n\n      const memberPaths = this.query.collectMemberNamesFor(() => this.query.evaluateSql(j.originalFrom, j.join.sql)).map(m => m.split('.'));\n\n      const invalidMembers = memberPaths.filter(m => !joinMap.has(m[0]) && m[0] !== j.originalTo);","sourceCodeStart":1081,"sourceCodeEnd":1117,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-schema-compiler/src/adapter/PreAggregations.ts#L1081-L1117","documentation":"For each join in a rollupJoin, Cube must find exactly one rollup pre-aggregation that contains all the dimension members of that join's side (fromMembers/toMembers). If no candidate pre-aggregation references every required member, this UserError fires and points at the rollupJoin definition — typically because abbreviated dimension paths were used instead of full Cube.dimension paths.","triggerScenarios":"A rollupJoin references pre-aggregations whose references.dimensions do not include all members of join.fromMembers or join.toMembers — e.g. members written as 'country' or 'Users.country' instead of the fully qualified path in the join definition or the pre-agg dimensions.","commonSituations":"Copy-pasted rollupJoin examples with partial member paths; pre-aggregation defined on a subset of dimensions not covering the join keys; renaming dimensions without updating rollupJoin member references.","solutions":["Use fully qualified member paths everywhere (e.g. Users.country, not 'country') in rollupJoin from/to and pre-aggregation dimensions.","Ensure each side's pre-aggregation includes the join key dimensions in its references.dimensions.","Check for renamed dimensions/members and update the rollupJoin definition accordingly.","Verify exactly one matching rollup exists per join side — zero triggers this error, more than one throws the 'Multiple rollups found' companion error."],"exampleFix":"// before\nrollupJoin: [{ from: 'Users', to: 'Orders', fromMembers: ['country'], toMembers: ['userCountry'] }]\n// after\nrollupJoin: [{ from: 'Users', to: 'Orders', fromMembers: ['Users.country'], toMembers: ['Orders.userCountry'] }]","handlingStrategy":"validation","validationCode":"// verify each rollupJoin member appears in the referenced pre-aggregation's dimensions\nconst isFullPath = (m) => /^[A-Za-z_]\\w*\\.[A-Za-z_]\\w*$/.test(m);\nfor (const j of rollupJoin) {\n  [...j.fromMembers, ...j.toMembers].forEach(m => {\n    if (!isFullPath(m)) throw new Error(`rollupJoin member '${m}' must be a full path like Users.country`);\n  });\n}","typeGuard":"const isFullyQualifiedMember = (m: string): m is `${string}.${string}` => /^[A-Za-z_]\\w*\\.[A-Za-z_]\\w*$/.test(m);","tryCatchPattern":"try { await cubeApi.load(query); } catch (e) { if (/No rollups found that can be used for a rollup join/.test(e.message)) {\n  const preAggName = e.message.match(/Check the \"([^\"]+)\"/)?.[1];\n  throw new Error(`Add full dimension paths to pre-aggregation '${preAggName}'`); } throw e; }","preventionTips":["Always use fully qualified member paths in rollupJoin from/to","Include join-key dimensions in each participating pre-aggregation","Update rollupJoin definitions when renaming dimensions","Compile all rollupJoins in a schema test before deploying"],"tags":["rollup-join","pre-aggregation","member-paths"],"backgroundTag":"rollup-join-member-not-found","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}