apache/druid · error · BadQueryContextException
Expected key [indexSpec] to be an indexSpec, but got [%s]
Error message
Expected key [indexSpec] to be an indexSpec, but got [%s]
What it means
MultiStageQueryContext.getIndexSpec decodes the 'indexSpec' query context key into an org.apache.druid.segment.indexing.IndexSpec, either from a JSON string or by converting a POJO/map. If both deserialization approaches fail, it throws badValueException stating the key must be 'an indexSpec'. The query is rejected at planning time.
Source
Thrown at multi-stage-query/src/main/java/org/apache/druid/msq/util/MultiStageQueryContext.java:754
/**
* Decodes {@link #CTX_INDEX_SPEC} from either a JSON-encoded string, or POJOs.
*/
@Nullable
@VisibleForTesting
static IndexSpec decodeIndexSpec(@Nullable final Object indexSpecObject, final ObjectMapper objectMapper)
{
try {
if (indexSpecObject == null) {
return null;
} else if (indexSpecObject instanceof String) {
return objectMapper.readValue((String) indexSpecObject, IndexSpec.class);
} else {
return objectMapper.convertValue(indexSpecObject, IndexSpec.class);
}
}
catch (Exception e) {
throw QueryContexts.badValueException(CTX_INDEX_SPEC, "an indexSpec", indexSpecObject);
}
}
/**
* This method is used to validate and get the taskLockType from the queryContext.
* If the queryContext does not contain the taskLockType, then {@link TaskLockType#EXCLUSIVE} is used for replace queries and
* {@link TaskLockType#SHARED} is used for insert queries.
* If the queryContext contains the taskLockType, then it is validated and returned.
*/
public static TaskLockType validateAndGetTaskLockType(QueryContext queryContext, boolean isReplaceQuery)
{
final boolean useConcurrentLocks = queryContext.getBoolean(
Tasks.USE_CONCURRENT_LOCKS,
Tasks.DEFAULT_USE_CONCURRENT_LOCKS
);
if (useConcurrentLocks) {
return isReplaceQuery ? TaskLockType.REPLACE : TaskLockType.APPEND;
}View on GitHub (pinned to 9b90983fd2)
Solutions
- Validate the JSON against IndexSpec fields: only bitmap type, bitmapUncompressedable options, dimensionCompression, metricCompression, and long/double encoding keys are accepted.
- Pass the indexSpec as a JSON string of a valid IndexSpec, e.g. '{"dimensionCompression":"lz4"}'.
- Remove the indexSpec key to use defaults if custom compression is not required.
Example fix
// before
queryContext.put("indexSpec", "{\"dimensionCompression\":\"snappy\"}"); // snappy not supported
// after
queryContext.put("indexSpec", "{\"dimensionCompression\":\"lz4\",\"metricCompression\":\"lz4\"}"); Defensive patterns
Strategy: validation
Validate before calling
Object idxSpec = queryContext.get("indexSpec");
if (idxSpec instanceof String) {
new ObjectMapper().readValue((String) idxSpec, IndexSpec.class); // throws early if invalid
} Try / catch
try {
IndexSpec spec = MultiStageQueryContext.getIndexSpec(queryContext, mapper);
} catch (QueryContextAwareException | BadJsonQueryException e) {
// inspect e.getMessage() for the offending indexSpec value
} Prevention
- Only use IndexSpec-supported fields (dimensionCompression, metricCompression, bitmap type, encodings).
- Copy indexSpec JSON from a known-good native ingestion spec validated on the same Druid version.
- Omit the key to accept defaults when custom compression is unnecessary.
When it happens
Trigger: Setting query context key 'indexSpec' to a value that does not deserialize to IndexSpec: an unknown property name, a wrong-typed field (e.g. dimensionCompression: 5), or a JSON string of a different object type.
Common situations: Users copy an ingestion-spec indexSpec snippet into the query context with incompatible fields (e.g. rollup-specific keys), misspell properties like 'dimensionCompression', or pass a map with nested objects that do not match IndexSpec's schema.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- No such outputChannelMode[%s]
- Expected key [%s] to be CSV or JSON array, but got [%s]
- Expected key [%s] to be a Granularity, but got [%s]
- unknown event type [%s]
- interrupted flushing elements from queue
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/af49ed47fccb2d43.
Report an issue: GitHub.