{"record":{"id":"fd3dfcca21773bea","repo":"apache/druid","slug":"empty-or-null-virtualcolumn-name","errorCode":null,"errorMessage":"Empty or null virtualColumn name","messagePattern":"Empty or null virtualColumn name","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/segment/VirtualColumns.java","lineNumber":112,"sourceCode":"  {\n    if (virtualColumns == null || virtualColumns.isEmpty()) {\n      return EMPTY;\n    }\n    return fromIterable(virtualColumns);\n  }\n\n  public static VirtualColumns create(VirtualColumn... virtualColumns)\n  {\n    return create(Arrays.asList(virtualColumns));\n  }\n\n  public static VirtualColumns fromIterable(Iterable<VirtualColumn> virtualColumns)\n  {\n    Map<String, VirtualColumn> withDotSupport = new HashMap<>();\n    Map<String, VirtualColumn> withoutDotSupport = new HashMap<>();\n    for (VirtualColumn vc : virtualColumns) {\n      if (Strings.isNullOrEmpty(vc.getOutputName())) {\n        throw new IAE(\"Empty or null virtualColumn name\");\n      }\n\n      if (vc.getOutputName().equals(ColumnHolder.TIME_COLUMN_NAME)) {\n        throw new IAE(\"virtualColumn name[%s] not allowed\", vc.getOutputName());\n      }\n\n      if (withDotSupport.containsKey(vc.getOutputName()) || withoutDotSupport.containsKey(vc.getOutputName())) {\n        throw new IAE(\"Duplicate virtualColumn name[%s]\", vc.getOutputName());\n      }\n\n      if (vc.usesDotNotation()) {\n        withDotSupport.put(vc.getOutputName(), vc);\n      } else {\n        withoutDotSupport.put(vc.getOutputName(), vc);\n      }\n    }\n    return new VirtualColumns(ImmutableList.copyOf(virtualColumns), withDotSupport, withoutDotSupport);\n  }","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/segment/VirtualColumns.java#L94-L130","documentation":"VirtualColumns.fromIterable validates each VirtualColumn's output name. A virtual column with a null or empty output name cannot be referenced in queries, so an IAE is thrown at construction time. Every virtual column must have a non-empty, unique output name distinct from __time.","triggerScenarios":"Calling VirtualColumns.fromIterable (or create) with a VirtualColumn whose getOutputName() returns null or \"\" — commonly a misconfigured expression or lookup virtual column in a query.","commonSituations":"Hand-written native queries omitting the expression outputName; deserialized queries from clients where the name field was dropped; programmatic query builders leaving names unset.","solutions":["Set an explicit outputName on every VirtualColumn in the query","Check query JSON for virtualColumns entries missing the \"name\"/outputName field","Update query-builder code to require a non-empty name (Objects.requireNonNull) before constructing columns"],"exampleFix":"// before\nExpressionVirtualColumn vc = new ExpressionVirtualColumn(null, \"x + 1\", ValueType.LONG, exprMacroTable);\n// after\nExpressionVirtualColumn vc = new ExpressionVirtualColumn(\"v:x_plus_1\", \"x + 1\", ValueType.LONG, exprMacroTable);","handlingStrategy":"validation","validationCode":"for (VirtualColumn vc : virtualColumns) {\n  if (Strings.isNullOrEmpty(vc.getOutputName())) {\n    throw new IllegalArgumentException(\"Virtual column requires non-empty outputName\");\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  VirtualColumns.fromIterable(cols);\n} catch (IAE e) {\n  log.error(e, \"Bad virtual column config\");\n}","preventionTips":["Always set outputName when constructing virtual columns","Validate query JSON schema before submitting","Add builder checks requiring non-empty names"],"tags":["virtual-column","validation","query"],"backgroundTag":"empty-required-field","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}