{"record":{"id":"b50e6bba7cda78eb","repo":"apache/druid","slug":"detected-duplicate-prefix-in-join-clauses-s","errorCode":null,"errorMessage":"Detected duplicate prefix in join clauses: [%s]","messagePattern":"Detected duplicate prefix in join clauses: \\[(.+?)\\]","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/segment/join/JoinPrefixUtils.java","lineNumber":93,"sourceCode":"\n  /**\n   * Check if any prefixes in the provided list duplicate or shadow each other.\n   *\n   * @param prefixes A mutable list containing the prefixes to check. This list will be sorted by descending\n   *                 string length.\n   */\n  public static void checkPrefixesForDuplicatesAndShadowing(\n      final List<String> prefixes\n  )\n  {\n    // this is a naive approach that assumes we'll typically handle only a small number of prefixes\n    prefixes.sort(DESCENDING_LENGTH_STRING_COMPARATOR);\n    for (int i = 0; i < prefixes.size(); i++) {\n      String prefix = prefixes.get(i);\n      for (int k = i + 1; k < prefixes.size(); k++) {\n        String otherPrefix = prefixes.get(k);\n        if (prefix.equals(otherPrefix)) {\n          throw new IAE(\"Detected duplicate prefix in join clauses: [%s]\", prefix);\n        }\n        if (isPrefixedBy(prefix, otherPrefix)) {\n          throw new IAE(\"Detected conflicting prefixes in join clauses: [%s, %s]\", prefix, otherPrefix);\n        }\n      }\n    }\n  }\n}\n","sourceCodeStart":75,"sourceCodeEnd":102,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/segment/join/JoinPrefixUtils.java#L75-L102","documentation":"JoinPrefixUtils.checkPrefixesForDuplicatesAndShadowing validates the set of prefixes used by join clauses before a query runs. Two joinable tables must not use the identical prefix because column references would be ambiguous, so an exact duplicate triggers this IllegalArgumentException.","triggerScenarios":"Passing a list of prefixes to checkPrefixesForDuplicatesAndShadowing where two entries are equal strings, e.g. two lookup/broadcast joins both declared with prefix 'l1.' in the same native query.","commonSituations":"Hand-written native JSON with multiple joins that reuse the same prefix; programmatic query builders that auto-number prefixes incorrectly; copying a join clause and forgetting to change its prefix.","solutions":["Rename one join's prefix so every join in the query has a distinct prefix (e.g. l1., l2., ...)","If the query is generated, ensure the generator assigns a unique prefix per joinable table","Search the native query JSON for duplicated 'prefix' fields and deduplicate"],"exampleFix":"// before\njoin prefixes: [\"l1.\", \"l1.\"]\n// after\njoin prefixes: [\"l1.\", \"l2.\"]","handlingStrategy":"validation","validationCode":"Set<String> seen = new HashSet<>();\nfor (String p : prefixes) { if (!seen.add(p)) throw new IllegalArgumentException(\"duplicate prefix \" + p); }\nJoinPrefixUtils.checkPrefixesForDuplicatesAndShadowing(new ArrayList<>(prefixes));","typeGuard":null,"tryCatchPattern":"try { checkPrefixes(...); } catch (IAE e) { throw new QueryPlanningException(\"duplicate join prefixes\", e); }","preventionTips":["Auto-generate prefixes with an incrementing counter per join","Run prefix validation in a unit test over every generated native query","Grep generated query JSON for duplicate prefix values before submission"],"tags":["java","join","druid","validation"],"backgroundTag":"conflicting-config-options","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}