apache/druid · error · IllegalArgumentException

Duplicate virtualColumn name

Error message

Duplicate virtualColumn name[%s]

What it means

Each VirtualColumn in a query must have a unique output name. fromIterable tracks names in maps and throws IAE when a duplicate name is encountered, because duplicated names would make column resolution ambiguous.

Solutions

  1. Rename the duplicate virtual columns to unique output names
  2. Deduplicate the virtualColumns list before building the query (e.g. by name using LinkedHashMap)
  3. Fix query-generation code to suffix names (v_expr_1, v_expr_2) when generating multiple columns

Example fix

// before
columns.add(new ExpressionVirtualColumn("v_x", "x * 2", ValueType.LONG, macros));
columns.add(new ExpressionVirtualColumn("v_x", "x * 3", ValueType.LONG, macros));
// after
columns.add(new ExpressionVirtualColumn("v_x_double", "x * 2", ValueType.LONG, macros));
columns.add(new ExpressionVirtualColumn("v_x_triple", "x * 3", ValueType.LONG, macros));
Defensive patterns

Strategy: validation

Validate before calling

Set<String> seen = new HashSet<>();
for (VirtualColumn vc : virtualColumns) {
  if (!seen.add(vc.getOutputName())) {
    throw new IllegalArgumentException("Duplicate virtual column name: " + vc.getOutputName());
  }
}

Try / catch

try {
  VirtualColumns.fromIterable(cols);
} catch (IAE e) {
  log.error(e, "Duplicate virtual column names in query");
}

Prevention

When it happens

Trigger: Calling VirtualColumns.fromIterable/create with two virtual columns sharing the same output name — e.g. two expression columns both named "v_expr", or an expression and lookup column with identical names.

Common situations: Query builders appending columns without deduplicating; merging query fragments client-side; copy-pasted query JSON reusing a name.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/segment/VirtualColumns.java:120

  {
    return create(Arrays.asList(virtualColumns));
  }

  public static VirtualColumns fromIterable(Iterable<VirtualColumn> virtualColumns)
  {
    Map<String, VirtualColumn> withDotSupport = new HashMap<>();
    Map<String, VirtualColumn> withoutDotSupport = new HashMap<>();
    for (VirtualColumn vc : virtualColumns) {
      if (Strings.isNullOrEmpty(vc.getOutputName())) {
        throw new IAE("Empty or null virtualColumn name");
      }

      if (vc.getOutputName().equals(ColumnHolder.TIME_COLUMN_NAME)) {
        throw new IAE("virtualColumn name[%s] not allowed", vc.getOutputName());
      }

      if (withDotSupport.containsKey(vc.getOutputName()) || withoutDotSupport.containsKey(vc.getOutputName())) {
        throw new IAE("Duplicate virtualColumn name[%s]", vc.getOutputName());
      }

      if (vc.usesDotNotation()) {
        withDotSupport.put(vc.getOutputName(), vc);
      } else {
        withoutDotSupport.put(vc.getOutputName(), vc);
      }
    }
    return new VirtualColumns(ImmutableList.copyOf(virtualColumns), withDotSupport, withoutDotSupport);
  }

  public static VirtualColumns nullToEmpty(@Nullable VirtualColumns virtualColumns)
  {
    return virtualColumns == null ? EMPTY : virtualColumns;
  }

  // For equals, hashCode, toString, and serialization:
  private final List<VirtualColumn> virtualColumns;

View on GitHub (pinned to 9b90983fd2)