{"record":{"id":"8e358a2d7e7d1715","repo":"cube-js/cube","slug":"measurename-references-cubes-that-lead-to-row","errorCode":null,"errorMessage":"'${measureName}' references cubes that lead to row multiplication. Please rewrite it using sub query.","messagePattern":"'(.+?)' references cubes that lead to row multiplication\\. Please rewrite it using sub query\\.","errorType":"validation","errorClass":"UserError","httpStatus":null,"severity":"error","filePath":"packages/cubejs-schema-compiler/src/adapter/BaseQuery.js","lineNumber":2607,"sourceCode":"        this.collectMemberNamesFor.bind(this),\n        'collectMemberNamesFor',\n      );\n\n      const nonViewMembers = memberNamesForMeasure\n        .map(member => this.memberInstanceByPath(member))\n        .filter(member => member.definition().ownedByCube);\n\n      const cubes = this.collectFrom(nonViewMembers, this.collectCubeNamesFor.bind(this), 'collectCubeNamesFor');\n      // Not using `collectJoinHintsFromMembers([measure])` because it would collect too many join hints from view\n      const joinHints = [\n        measure.joinHint,\n        ...this.collectJoinHintsFromMembers(nonViewMembers),\n      ];\n      if (R.any(cubeName => keyCubeName !== cubeName, cubes)) {\n        const measuresJoin = this.joinGraph.buildJoin(joinHints);\n        if (measuresJoin.multiplicationFactor[keyCubeName]) {\n          const measureName = measure.isMemberExpression ? measure.expressionName : measure.measure;\n          throw new UserError(\n            `'${measureName}' references cubes that lead to row multiplication. Please rewrite it using sub query.`\n          );\n        }\n        return true;\n      }\n      return false;\n    }).reduce((a, b) => a || b);\n  }\n\n  aggregateSubQueryMeasureJoin(keyCubeName, measures, measuresJoin, primaryKeyDimensions, measureSubQueryDimensions) {\n    return this.ungroupedMeasureSelect(() => this.withCubeAliasPrefix(`${keyCubeName}_measure_join`,\n      () => {\n        const columns = primaryKeyDimensions.map(p => p.selectColumns()).concat(measures.map(m => m.selectColumns()))\n          .filter(s => !!s).join(', ');\n        return `SELECT ${columns} FROM ${this.joinQuery(measuresJoin, measureSubQueryDimensions)}`;\n      }));\n  }\n","sourceCodeStart":2589,"sourceCodeEnd":2625,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js#L2589-L2625","documentation":"Similar to the measure-sub-query check but per-measure: when building the join for a single measure's select, if the measure references cubes other than the key cube and the built join multiplies the key cube's rows, Cube throws this UserError advising a sub query rewrite.","triggerScenarios":"buildJoinForMeasureSelect / buildMeasureGroupedCubePath: measure (or its view members) reference other cubes; R.any shows cubes differ from keyCubeName; joinGraph.buildJoin(joinHints).multiplicationFactor[keyCubeName] is truthy.","commonSituations":"A measure in a cube that pulls ${OtherCube.field} across a has-many join; view members whose join path fans out; schema refactors that turn a belongs-to join into has-many.","solutions":["Move the referenced field into the key cube (denormalize) or use a rollup.","Rewrite the measure as a sub query measure so its aggregation is isolated.","Change the join direction so the referenced cube is on the one-side of the relationship.","Add a view-level alias/join path that avoids the multiplied cube."],"exampleFix":"// before (measure in Orders referencing Items across has-many)\nmeasures: { totalItems: { type: 'count', sql: '${Items.id}' } }\n// after (sub query measure)\nmeasures: {\n  totalItems: {\n    type: 'number',\n    sql: 'SELECT count(*) FROM ${Items.sql} WHERE ${Items.order_id} = ${Orders.id}',\n    subQuery: true\n  }\n}","handlingStrategy":"try-catch","validationCode":"null","typeGuard":null,"tryCatchPattern":"try {\n  return await cubeApi.load(query);\n} catch (e) {\n  if (/references cubes that lead to row multiplication/.test(e.message)) {\n    const measure = e.message.match(/'(.*?)' references/)?.[1];\n    console.error(`Rewrite ${measure} using a sub query`);\n  }\n  throw e;\n}","preventionTips":["Keep each measure's references within its own cube or one-side joins","Rewrite cross-cube measures as subQuery measures at schema design time","Review join direction changes in code review for fan-out risk"],"tags":["fanout","join-multiplication","measures"],"backgroundTag":"row-multiplication-fanout","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}