{"record":{"id":"5cd6d89d3e5c3e0b","repo":"cube-js/cube","slug":"can-not-construct-joins-for-the-query-potential-l","errorCode":null,"errorMessage":"Can not construct joins for the query, potential loop detected","messagePattern":"Can not construct joins for the query, potential loop detected","errorType":"exception","errorClass":"UserError","httpStatus":null,"severity":"error","filePath":"packages/cubejs-schema-compiler/src/adapter/BaseQuery.js","lineNumber":458,"sourceCode":"      prevJoin = newJoin;\n      newJoin = this.joinGraph.buildJoin(allJoinHints);\n      const allJoinHintsFlatten = new Set(allJoinHints.flat());\n      const joinMembersJoinHints = this.collectJoinHintsFromMembers(this.joinMembersFromJoin(newJoin));\n\n      const iterationCollectedHints = joinMembersJoinHints.filter(j => !allJoinHintsFlatten.has(j));\n      newJoinHintsCollectedCnt = iterationCollectedHints.length;\n      cnt++;\n      if (newJoin && newJoin.joins.length > 0) {\n        // Even if there is no join tree changes, we still\n        // push correctly ordered join hints, collected from the resolving of members of join tree\n        // upfront the all existing query members. This ensures the correct cube join order\n        // with transitive joins even if they are already presented among query members.\n        newCollectedHints = this.enrichedJoinHintsFromJoinTree(newJoin, joinMembersJoinHints);\n      }\n    } while (newJoin?.joins.length > 0 && !this.isJoinTreesEqual(prevJoin, newJoin) && cnt < 10000 && newJoinHintsCollectedCnt > 0);\n\n    if (cnt >= 10000) {\n      throw new UserError('Can not construct joins for the query, potential loop detected');\n    }\n\n    return this.joinGraph.buildJoin(constructJH());\n  }\n\n  cacheValue(key, fn, { contextPropNames, inputProps, cache } = {}) {\n    const currentContext = this.safeEvaluateSymbolContext();\n    if (contextPropNames) {\n      const contextKey = {};\n      for (const element of contextPropNames) {\n        contextKey[element] = currentContext[element];\n      }\n      key = key.concat([JSON.stringify(contextKey)]);\n    }\n    const { value, resultProps } = (cache || this.compilerCache).cache(\n      key,\n      () => {\n        if (inputProps) {","sourceCodeStart":440,"sourceCodeEnd":476,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js#L440-L476","documentation":"BaseQuery builds the join graph by iteratively collecting join hints until the tree stabilizes. As a safety net, if the iteration exceeds 10000 rounds (meaning join trees keep changing and never converge — a cyclic or contradictory join definition), it throws this UserError instead of hanging.","triggerScenarios":"Cubes whose join definitions form a cycle or mutually referencing joins that keep generating new hints each round, so joinTreeForHints never reaches a fixed point within 10000 iterations.","commonSituations":"Large schemas with transitive joins between many cubes, mistakenly defining A->B, B->C, C->A style loops, or joins referencing members that pull in more joins indefinitely after schema edits.","solutions":["Review joins in the data model for cycles and remove the loop","Break transitive chains by adding the needed direct join explicitly","Reduce the members in the query to isolate which cubes cause the loop","Test the join path with a minimal query adding one cube at a time"],"exampleFix":"// before (cycle in schema)\njoins: Orders: { sql: \"${Carts.id} = ${Orders.cartId}\", ... }\njoins: Carts: { sql: \"${Orders.id} = ${Carts.orderId}\", ... }\n// after (single direction)\njoins: Carts: { sql: \"${Orders.id} = ${Carts.orderId}\", ... }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const data = await cubeApi.load(query);\n} catch (e) {\n  if (/potential loop detected/.test(e.message)) {\n    console.error('Join cycle in schema; reduce members or fix joins', e.message);\n    throw new Error('Query cannot be planned: join loop in schema');\n  }\n  throw e;\n}","preventionTips":["Keep join definitions acyclic: each pair of cubes joined in one direction only","Review joins after schema changes; add explicit joins for needed transitive paths","Test large multi-cube queries incrementally during schema development"],"tags":["joins","schema","graph"],"backgroundTag":"join-graph-loop","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}