{"record":{"id":"b9fe0602dbbe6cc4","repo":"apache/druid","slug":"column-s-does-not-start-with-prefix-s","errorCode":null,"errorMessage":"Column[%s] does not start with prefix[%s]","messagePattern":"Column\\[(.+?)\\] does not start with prefix\\[(.+?)\\]","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/segment/join/JoinPrefixUtils.java","lineNumber":72,"sourceCode":"  }\n\n  public static boolean isPrefixedBy(final String columnName, final String prefix)\n  {\n    return columnName.length() > prefix.length() && columnName.startsWith(prefix);\n  }\n\n  /**\n   * Removes the prefix on {@code columnName}. Must only be called if the column name is actually prefixed; i.e.,\n   * if {@link #isPrefixedBy(String, String)} would return true on the same arguments.\n   *\n   * @throws IllegalArgumentException if columnName does not start with prefix\n   */\n  public static String unprefix(final String columnName, final String prefix)\n  {\n    if (isPrefixedBy(columnName, prefix)) {\n      return columnName.substring(prefix.length());\n    } else {\n      throw new IAE(\"Column[%s] does not start with prefix[%s]\", columnName, prefix);\n    }\n  }\n\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++) {","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/segment/join/JoinPrefixUtils.java#L54-L90","documentation":"JoinPrefixUtils.unprefix strips a join-table prefix (e.g. 'l1.') from a column name, but only if the column actually starts with that prefix. If it does not, Druid throws this IllegalArgumentException because returning the original name would silently mis-map columns in a join.","triggerScenarios":"Calling JoinPrefixUtils.unprefix(columnName, prefix) where columnName does not begin with prefix; typically reached when join column names in a join clause were computed with a different prefix than the one used during unprefixing.","commonSituations":"Misconfigured join condition column names in a native join (prefix mismatch between the join clause and the table prefix), SQL plan translation bugs where right-column identifiers were not prefixed, or user-supplied native JSON joins where the equated column name lacks the declared prefix.","solutions":["Check the join clause and the prefix declared on the joinable dataSource match the column name being unprefixed","Remove or correct the prefix parameter so it equals the actual prefix of the column name","Ensure all right-side columns in equi conditions are prefixed consistently when building the native query"],"exampleFix":"// before\nString name = JoinPrefixUtils.unprefix(\"country_code\", \"l1.\"); // IAE\n// after\nString name = JoinPrefixUtils.unprefix(\"l1.country_code\", \"l1.\");","handlingStrategy":"validation","validationCode":"if (!columnName.startsWith(prefix)) { throw new IllegalArgumentException(\"Column \" + columnName + \" lacks prefix \" + prefix); }\nString bare = JoinPrefixUtils.unprefix(columnName, prefix);","typeGuard":"boolean isPrefixed(String col, String prefix) { return col != null && prefix != null && col.startsWith(prefix); }","tryCatchPattern":"try { return JoinPrefixUtils.unprefix(columnName, prefix); } catch (IllegalArgumentException e) { log.error(\"prefix mismatch: {} vs {}\", columnName, prefix); throw e; }","preventionTips":["Always derive the prefix from the same join clause object used to build column names","Unit-test join clause construction so prefixes and column names come from one source","Assert isPrefixedBy(columnName, prefix) before unprefixing in custom code"],"tags":["java","join","column-naming","druid"],"backgroundTag":"invalid-argument-value","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"}