{"record":{"id":"1e91bac6c0038af8","repo":"apache/druid","slug":"virtualcolumn-name-s-not-allowed","errorCode":null,"errorMessage":"virtualColumn name[%s] not allowed","messagePattern":"virtualColumn name\\[(.+?)\\] not allowed","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/segment/VirtualColumns.java","lineNumber":116,"sourceCode":"    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  }\n\n  public static VirtualColumns nullToEmpty(@Nullable VirtualColumns virtualColumns)\n  {\n    return virtualColumns == null ? EMPTY : virtualColumns;","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/segment/VirtualColumns.java#L98-L134","documentation":"Virtual column output names must not collide with the special __time column, since __time is always present in segments and shadowing it would be ambiguous. fromIterable throws IAE if any virtual column's output name equals ColumnHolder.TIME_COLUMN_NAME.","triggerScenarios":"Constructing VirtualColumns with a virtual column whose getOutputName() is \"__time\" — e.g. an expression column intentionally named __time in a native query.","commonSituations":"Custom queries trying to override __time with a computed timestamp; generated queries colliding names; users unaware __time is reserved.","solutions":["Rename the virtual column to something like \"v_time\" and reference that name in the query","Compute time transforms via the __time column with an extraction/expression in selectors instead of shadowing it","Update query-generation code to reject \"__time\" as a virtual column name"],"exampleFix":"// before\nnew ExpressionVirtualColumn(\"__time\", \"timestamp_shift(__time, 'P1D')\", ValueType.LONG, macros)\n// after\nnew ExpressionVirtualColumn(\"v_shifted_time\", \"timestamp_shift(__time, 'P1D')\", ValueType.LONG, macros)","handlingStrategy":"validation","validationCode":"if (ColumnHolder.TIME_COLUMN_NAME.equals(vc.getOutputName())) {\n  throw new IllegalArgumentException(\"__time is reserved and cannot be a virtual column name\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  VirtualColumns.fromIterable(cols);\n} catch (IAE e) {\n  log.error(e, \"Virtual column name conflicts with __time\");\n}","preventionTips":["Treat __time as reserved in query generation","Use prefixed names (v_) for virtual columns","Reject reserved names in query builders"],"tags":["virtual-column","reserved-name","query"],"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-17T15:17:12.973Z"}