{"record":{"id":"bac8aef171295cde","repo":"apache/druid","slug":"granularity-s-is-not-supported","errorCode":null,"errorMessage":"Granularity [%s] is not supported","messagePattern":"Granularity \\[(.+?)\\] is not supported","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"multi-stage-query/src/main/java/org/apache/druid/msq/querykit/QueryKitUtils.java","lineNumber":214,"sourceCode":"   *\n   * @throws IllegalArgumentException if the provided granularity is not supported\n   */\n  @Nullable\n  public static VirtualColumn makeSegmentGranularityVirtualColumn(\n      final ObjectMapper jsonMapper,\n      final QueryContext queryContext\n  )\n  {\n    final Granularity segmentGranularity =\n        QueryKitUtils.getSegmentGranularityFromContext(jsonMapper, queryContext.asMap());\n    final String timeColumnName = queryContext.getString(QueryKitUtils.CTX_TIME_COLUMN_NAME);\n\n    if (timeColumnName == null || Granularities.ALL.equals(segmentGranularity)) {\n      return null;\n    }\n\n    if (!(segmentGranularity instanceof PeriodGranularity)) {\n      throw new IAE(\"Granularity [%s] is not supported\", segmentGranularity);\n    }\n\n    final PeriodGranularity periodSegmentGranularity = (PeriodGranularity) segmentGranularity;\n\n    if (periodSegmentGranularity.getOrigin() != null\n        || !periodSegmentGranularity.getTimeZone().equals(DateTimeZone.UTC)) {\n      throw new IAE(\"Granularity [%s] is not supported\", segmentGranularity);\n    }\n\n    return new ExpressionVirtualColumn(\n        QueryKitUtils.SEGMENT_GRANULARITY_COLUMN,\n        StringUtils.format(\n            \"timestamp_floor(%s, %s)\",\n            CalciteSqlDialect.DEFAULT.quoteIdentifier(timeColumnName),\n            Calcites.escapeStringLiteral((periodSegmentGranularity).getPeriod().toString())\n        ),\n        ColumnType.LONG,\n        new ExprMacroTable(Collections.singletonList(new TimestampFloorExprMacro()))","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/multi-stage-query/src/main/java/org/apache/druid/msq/querykit/QueryKitUtils.java#L196-L232","documentation":"Thrown by QueryKitUtils.makeSegmentGranularityVirtualColumn when the configured segment granularity is not Granularities.ALL and not a PeriodGranularity. MSQ builds a time-flooring virtual column from a PeriodGranularity; other Granularity implementations (or period granularities with a non-UTC time zone / custom origin) cannot be turned into the bucketing column.","triggerScenarios":"Setting the segment granularity context to a granularity type other than PeriodGranularity (e.g. DurationGranularity or a custom implementation), or a PeriodGranularity with an explicit origin or non-UTC timeZone, then generating the __granularity virtual column.","commonSituations":"Custom granularity objects supplied programmatically; period granularities with timezone offsets for local-time bucketing (unsupported by this path); configs migrated from ingestion specs using duration-based granularities.","solutions":["Use a plain period granularity in UTC with no origin, e.g. {\"type\":\"period\",\"period\":\"P1D\"} (equivalent to \"day\").","Use a standard named granularity such as \"hour\", \"day\", \"month\".","If local-timezone bucketing is needed, precompute the bucket in SQL (e.g. TIME_FLOOR with TIME_SHIFT) instead of relying on segment granularity.","Remove a custom origin from the period granularity or align data upstream."],"exampleFix":"// before\ncontext.put(\"segmentGranularity\", new DurationGranularity(86400000L, 0L));\n// after\ncontext.put(\"segmentGranularity\", new PeriodGranularity(Period.days(1), null, DateTimeZone.UTC));","handlingStrategy":"validation","validationCode":"Granularity g = segmentGranularity;\nif (g != null && !Granularities.ALL.equals(g) && !(g instanceof PeriodGranularity)) {\n  throw new IllegalArgumentException(\"Segment granularity must be a UTC period granularity\");\n}\nif (g instanceof PeriodGranularity) {\n  PeriodGranularity pg = (PeriodGranularity) g;\n  if (pg.getOrigin() != null || !pg.getTimeZone().equals(DateTimeZone.UTC)) {\n    throw new IllegalArgumentException(\"Period granularity must have null origin and UTC timezone\");\n  }\n}","typeGuard":"boolean isSupportedSegmentGranularity(Object g) {\n  return g == null || Granularities.ALL.equals(g)\n      || (g instanceof PeriodGranularity\n          && ((PeriodGranularity) g).getOrigin() == null\n          && DateTimeZone.UTC.equals(((PeriodGranularity) g).getTimeZone()));\n}","tryCatchPattern":"try {\n  VirtualColumn vc = QueryKitUtils.makeSegmentGranularityVirtualColumn(timeCol, granularity);\n} catch (IllegalArgumentException e) {\n  granularity = Granularities.DAY; // fall back to a supported period granularity\n}","preventionTips":["Use standard UTC period granularities without origin for segment granularity.","Avoid DurationGranularity or custom Granularity implementations in MSQ context.","Handle timezone-specific bucketing in SQL expressions instead."],"tags":["druid","msq","granularity","unsupported"],"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-14T11:17:12.474Z"}