{"record":{"id":"54c462d600e946e3","repo":"apache/druid","slug":"expected-key-s-to-be-a-granularity-but-got-s","errorCode":null,"errorMessage":"Expected key [%s] to be a Granularity, but got [%s]","messagePattern":"Expected key \\[(.+?)\\] to be a Granularity, but got \\[(.+?)\\]","errorType":"exception","errorClass":"BadQueryContextException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/QueryContext.java","lineNumber":251,"sourceCode":"  }\n\n  public <E extends Enum<E>> E getEnum(String key, Class<E> clazz, E defaultValue)\n  {\n    return QueryContexts.getAsEnum(key, get(key), clazz, defaultValue);\n  }\n\n  public Granularity getGranularity(String key, ObjectMapper jsonMapper)\n  {\n    final String granularityString = getString(key);\n    if (granularityString == null) {\n      return null;\n    }\n\n    try {\n      return jsonMapper.readValue(granularityString, Granularity.class);\n    }\n    catch (IOException e) {\n      throw QueryContexts.badTypeException(key, \"a Granularity\", granularityString);\n    }\n  }\n\n  public boolean isDebug()\n  {\n    return getBoolean(QueryContexts.ENABLE_DEBUG, QueryContexts.DEFAULT_ENABLE_DEBUG);\n  }\n\n  public boolean isBySegment()\n  {\n    return isBySegment(QueryContexts.DEFAULT_BY_SEGMENT);\n  }\n\n  public boolean isBySegment(boolean defaultValue)\n  {\n    return getBoolean(QueryContexts.BY_SEGMENT_KEY, defaultValue);\n  }\n","sourceCodeStart":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/QueryContext.java#L233-L269","documentation":"QueryContext.getGranularity parses a query context key into a Granularity (e.g. 'all', 'day', or a period string) using the object mapper. When the string cannot be deserialized to a Granularity, it throws QueryContexts.badTypeException declaring the key must be 'a Granularity'. The query fails during context validation.","triggerScenarios":"Setting a granularity-typed context key (e.g. timestampResultFieldGranularity) to a value like 'daily', 'dayly', or '1 day' that Jackson cannot map to Granularity; called from timestampResultFieldGranularity.","commonSituations":"Users confuse Granularity names with SQL interval syntax ('day' is valid, 'daily' and '7 days' are not), or pass a JSON object where a string like '{\"type\":\"period\",\"period\":\"P1D\"}' with wrong fields is given.","solutions":["Use a valid simple granularity string: 'second', 'minute', 'fifteen_minute', 'hour', 'day', 'week', 'month', 'quarter', 'year', or 'all'.","For custom periods, pass a valid JSON duration granularity: {\"type\":\"period\",\"period\":\"P1D\"}.","Check the error message: it includes the offending value; fix the exact string you passed."],"exampleFix":"// before\nqueryContext.put(\"timestampResultFieldGranularity\", \"daily\");\n// after\nqueryContext.put(\"timestampResultFieldGranularity\", \"day\");","handlingStrategy":"validation","validationCode":"Set<String> valid = Set.of(\"second\",\"minute\",\"fifteen_minute\",\"thirty_minute\",\"hour\",\"day\",\"week\",\"month\",\"quarter\",\"year\",\"all\");\nString g = (String) queryContext.get(\"timestampResultFieldGranularity\");\nif (g != null && !valid.contains(g.trim().toLowerCase()) && !g.trim().startsWith(\"{\")) {\n  throw new IllegalArgumentException(\"Not a Granularity: \" + g);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use canonical Granularity names ('day', 'hour'), never adjectives like 'daily'.","For custom periods, use a duration-granularity JSON object ({\"type\":\"period\",\"period\":\"P1D\"}).","Validate context values with Granularity.fromString before issuing the query in custom tooling."],"tags":["druid","query-context","granularity","deserialization"],"backgroundTag":"invalid-config-value","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}