{"record":{"id":"c950c64e13baba37","repo":"apache/druid","slug":"expected-key-s-to-be-a-string-but-got-s","errorCode":null,"errorMessage":"Expected key [%s] to be a String, but got [%s]","messagePattern":"Expected key \\[(.+?)\\] to be a String, but got \\[(.+?)\\]","errorType":"validation","errorClass":"BadQueryContextException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/QueryContexts.java","lineNumber":325,"sourceCode":"\n  @SuppressWarnings(\"unused\") // To keep IntelliJ inspections happy\n  public static float parseFloat(Map<String, Object> context, String key, float defaultValue)\n  {\n    return getAsFloat(key, context.get(key), defaultValue);\n  }\n\n  public static String getAsString(\n      final String key,\n      final Object value,\n      final String defaultValue\n  )\n  {\n    if (value == null) {\n      return defaultValue;\n    } else if (value instanceof String) {\n      return (String) value;\n    }\n    throw badTypeException(key, \"a String\", value);\n  }\n\n  @Nullable\n  public static Boolean getAsBoolean(\n      final String key,\n      final Object value\n  )\n  {\n    if (value == null) {\n      return null;\n    } else if (value instanceof String) {\n      return Boolean.parseBoolean((String) value);\n    } else if (value instanceof Boolean) {\n      return (Boolean) value;\n    }\n    throw badTypeException(key, \"a Boolean\", value);\n  }\n","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/QueryContexts.java#L307-L343","documentation":"QueryContexts.getAs(String key, Object value) throws this when a query context value must be read as a String but the stored object is neither null nor a String. Druid query contexts are loosely-typed Map<String, Object> objects (often deserialized from JSON), so the context getters coerce values and fail loudly when the type cannot be coerced. The exception names the context key and the actual runtime type of the value.","triggerScenarios":"Calling QueryContexts.getAs(key, context.get(key)) (or query.getContextValue overloads that route through it) when the context map holds a non-String, non-null object for that key, e.g. a Boolean, Number, or nested Map/List produced by JSON deserialization of the query context.","commonSituations":"A client sends a context key as a JSON object/array or a number while the query path expects a string (e.g. sqlQueryId, native query ids, or string-typed context parameters); a downstream caller puts a typed object into the context map where earlier versions accepted strings; programmatic query construction setting context.put(key, someObject).","solutions":["Inspect the query context JSON sent by the client and change the offending key's value to a JSON string (quote it).","If building queries programmatically, put a String into the context: context.put(key, String.valueOf(value)).","Use QueryContexts.override(context, key, stringValue) to replace the bad value before executing.","If the value legitimately is not a string, read it with the matching getter (getAsBoolean/getAsInt/getAsLong) instead of getAs."],"exampleFix":"// before\ncontext.put(\"queryId\", 12345); // Integer\nString id = query.getContextValue(\"queryId\"); // throws\n// after\ncontext.put(\"queryId\", \"12345\");\nString id = query.getContextValue(\"queryId\");","handlingStrategy":"type-guard","validationCode":"Object v = context.get(key);\nif (v != null && !(v instanceof String)) {\n  throw new IllegalArgumentException(\"Context key '\" + key + \"' must be a string, got: \" + v.getClass().getSimpleName());\n}","typeGuard":"boolean isContextString(Object v) { return v == null || v instanceof String; }","tryCatchPattern":"try {\n  return QueryContexts.getAs(key, context.get(key));\n} catch (IllegalArgumentException e) {\n  LOG.warn(e, \"Bad type for context key [%s], using default\", key);\n  return defaultValue;\n}","preventionTips":["Always send string-typed context keys as quoted JSON strings.","Use QueryContexts.override() with typed values instead of raw map puts.","Validate client-submitted query context against the query type's documented context schema before execution."],"tags":["druid","query-context","type-mismatch","string"],"backgroundTag":"type-mismatch","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"}