apache/druid · error · BadQueryContextException

Expected key [%s] to be CSV or JSON array, but got [%s]

Error message

Expected key [%s] to be CSV or JSON array, but got [%s]

What it means

Query context key parsing in MultiStageQueryContext expects a value that is either a comma-separated string or a JSON array string. When Jackson fails to parse the string as a JSON array (JsonProcessingException) in the JSON branch, Druid throws QueryContexts.badValueException stating the key must be a CSV or JSON array. This is a query-context validation error, so the query is rejected before execution.

Source

Thrown at multi-stage-query/src/main/java/org/apache/druid/msq/util/MultiStageQueryContext.java:720

    return queryContext.getInt(CTX_SEGMENT_LOAD_AHEAD_COUNT);
  }

  /**
   * Decodes a list from either a JSON or CSV string.
   */
  @VisibleForTesting
  static List<String> decodeList(final String keyName, @Nullable final String listString)
  {
    if (listString == null) {
      return Collections.emptyList();
    } else if (LOOKS_LIKE_JSON_ARRAY.matcher(listString).matches()) {
      try {
        // Not caching this ObjectMapper in a static, because we expect to use it infrequently (once per INSERT
        // query that uses this feature) and there is no need to keep it around longer than that.
        return new ObjectMapper().readValue(listString, new TypeReference<>() {});
      }
      catch (JsonProcessingException e) {
        throw QueryContexts.badValueException(keyName, "CSV or JSON array", listString);
      }
    } else {
      final RFC4180Parser csvParser = new RFC4180ParserBuilder().withSeparator(',').build();

      try {
        return Arrays.stream(csvParser.parseLine(listString))
                     .filter(s -> s != null && !s.isEmpty())
                     .map(String::trim)
                     .collect(Collectors.toList());
      }
      catch (IOException e) {
        throw QueryContexts.badValueException(keyName, "CSV or JSON array", listString);
      }
    }
  }

  /**
   * Decodes {@link #CTX_INDEX_SPEC} from either a JSON-encoded string, or POJOs.

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Pass the value as a simple comma-separated CSV string instead, e.g. "a,b,c", which avoids the JSON parser entirely.
  2. If using JSON, ensure it is a valid JSON array literal with double quotes: ["a","b"] with no trailing commas or single quotes.
  3. Verify the client is not double-encoding the value (a string containing escaped quotes like \" indicates double encoding).

Example fix

// before
queryContext.put("partitionKeys", "[\"country\", 'region]");
// after
queryContext.put("partitionKeys", "country,region");
Defensive patterns

Strategy: validation

Validate before calling

String v = (String) queryContext.get("myListKey");
if (v != null && v.trim().startsWith("[")) {
  try { new ObjectMapper().readValue(v, List.class); }
  catch (JsonProcessingException e) { throw new IllegalArgumentException("key must be CSV or a valid JSON array: " + v); }
}

Prevention

When it happens

Trigger: Calling MultiStageQueryContext.getListStringFromContext-style parsing (e.g. for CTX_TASK_STORAGE_DIR or partitioning keys) with a query context value like '{"a":1}' or a malformed JSON string such as '["a", ' that is not valid JSON, in the branch where the value is treated as JSON rather than CSV.

Common situations: Users pass the context value as JSON via a client that double-encodes or single-quotes strings (e.g. '["a","b"]' with unescaped quotes), or a BI tool submits the context key as an object rather than a JSON-encoded string.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/a5be68e6d6a0a302. Report an issue: GitHub.