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
- Rename the duplicate virtual columns to unique output names
- Deduplicate the virtualColumns list before building the query (e.g. by name using LinkedHashMap)
- 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
- Deduplicate virtualColumns by outputName before query construction
- Use unique generated names (suffix counters) in query builders
- Review hand-written query JSON for repeated names
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
- Empty or null virtualColumn name
- virtualColumn name[ ] not allowed
- Cannot handle datasource
- Cannot handle subquery
- Cannot have a null/empty columns
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)