apache/druid · error · IllegalArgumentException

Join clause cannot have null or empty prefix

Error message

Join clause cannot have null or empty prefix

What it means

JoinPrefixUtils.validatePrefix checks that a join clause prefix is usable: a null or empty prefix cannot namespace the joinable's columns, so it throws this IAE. Prefixes exist to qualify joined column names and prevent collisions.

Solutions

  1. Set a non-empty prefix on the JoinableClause (or use the joinable's name via the prefix field in the native join)
  2. In native JSON joins, populate "prefix" (e.g. "j0.", "j1.") for right-side joinables
  3. If generating queries programmatically, default the prefix to a unique identifier per clause

Example fix

// before
{"rightSubQuery": ..., "prefix": null, ...}
// after
{"rightSubQuery": ..., "prefix": "j0.", ...}
Defensive patterns

Strategy: validation

Validate before calling

if (prefix == null || prefix.isEmpty()) throw new IllegalArgumentException("Join prefix required");

Try / catch

try { JoinPrefixUtils.validatePrefix(prefix); } catch (IAE e) { if (e.getMessage().contains("null or empty")) { /* set prefix */ } throw e; }

Prevention

When it happens

Trigger: Creating a JoinableClause or validating a join in the query planner with prefix null or "", e.g. building joins from parsed SQL/subquery references without an explicit prefix.

Common situations: Programmatic native query construction omitting the prefix field; planner bugs where subquery join prefixes are not assigned; hand-written native JSON with "prefix": "".

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/7ef81ba0f73a4154. Report an issue: GitHub.

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/segment/join/JoinPrefixUtils.java:44

import java.util.Comparator;
import java.util.List;

/**
 * Utility class for working with prefixes in join operations
 */
public class JoinPrefixUtils
{
  private static final Comparator<String> DESCENDING_LENGTH_STRING_COMPARATOR = (s1, s2) ->
      Integer.compare(s2.length(), s1.length());

  /**
   * Checks that "prefix" is a valid prefix for a join clause (see {@link JoinableClause#getPrefix()}) and, if so,
   * returns it. Otherwise, throws an exception.
   */
  public static String validatePrefix(@Nullable final String prefix)
  {
    if (prefix == null || prefix.isEmpty()) {
      throw new IAE("Join clause cannot have null or empty prefix");
    } else if (isPrefixedBy(ColumnHolder.TIME_COLUMN_NAME, prefix) || ColumnHolder.TIME_COLUMN_NAME.equals(prefix)) {
      throw new IAE(
          "Join clause cannot have prefix[%s], since it would shadow %s",
          prefix,
          ColumnHolder.TIME_COLUMN_NAME
      );
    } else {
      return prefix;
    }
  }

  public static boolean isPrefixedBy(final String columnName, final String prefix)
  {
    return columnName.length() > prefix.length() && columnName.startsWith(prefix);
  }

  /**
   * Removes the prefix on {@code columnName}. Must only be called if the column name is actually prefixed; i.e.,

View on GitHub (pinned to 9b90983fd2)