elastic/elasticsearch · error · XContentParseException

Failed to parse list: expecting ${XContentParser.Token.STAR

Error message

Failed to parse list:  expecting ${XContentParser.Token.START_ARRAY} but got ${token}

What it means

Thrown by AbstractXContentParser.skipToListStart when the parser is asked to read a list (via list() or listOrderedMap()) but, after skipping an optional FIELD_NAME token, the current token is not START_ARRAY. The parser expected the JSON position to be at (or just before) an array but found a scalar, object, or end token instead.

Source

Thrown at libs/x-content/src/main/java/org/elasticsearch/xcontent/support/AbstractXContentParser.java:451

        }
        if (token == XContentParser.Token.START_OBJECT) {
            return parser.nextFieldName();
        }
        return token == Token.FIELD_NAME ? parser.currentName() : null;
    }

    // Skips the current parser to the next array start. Assumes that the parser is either positioned before an array field's name token or
    // on the start array token.
    private static void skipToListStart(XContentParser parser) throws IOException {
        Token token = parser.currentToken();
        if (token == null) {
            token = parser.nextToken();
        }
        if (token == XContentParser.Token.FIELD_NAME) {
            token = parser.nextToken();
        }
        if (token != XContentParser.Token.START_ARRAY) {
            throw new XContentParseException(
                parser.getTokenLocation(),
                "Failed to parse list:  expecting " + XContentParser.Token.START_ARRAY + " but got " + token
            );
        }
    }

    // read a list without bounds checks, assuming the current parser is always on an array start
    private static List<Object> readListUnsafe(XContentParser parser, Supplier<Map<String, Object>> mapFactory) throws IOException {
        assert parser.currentToken() == Token.START_ARRAY;
        ArrayList<Object> list = new ArrayList<>();
        for (Token token = parser.nextToken(); token != null && token != XContentParser.Token.END_ARRAY; token = parser.nextToken()) {
            list.add(readValueUnsafe(token, parser, mapFactory));
        }
        return list;
    }

    public static Object readValue(XContentParser parser, Supplier<Map<String, Object>> mapFactory) throws IOException {
        return readValueUnsafe(parser.currentToken(), parser, mapFactory);

View on GitHub (pinned to db6a809a66)

Solutions

  1. Normalize the producer to always emit the field as an array (even single-element arrays).
  2. In manual parser code, check currentToken() before calling list() and handle VALUE_*/START_OBJECT branches explicitly.
  3. Use the parser's `ensureExpectedToken` or token-aware helpers to guard list reads.
  4. If you do not control the producer, pre-process the JSON to wrap scalars in arrays.

Example fix

// before: list() called on a scalar token
Token t = parser.currentToken();
List<Object> items = parser.list();
// after: guard the token
Token t = parser.currentToken();
if (t != Token.START_ARRAY) { throw new IllegalArgumentException("expected array at " + parser.getTokenLocation()); }
List<Object> items = parser.list();
Defensive patterns

Strategy: type-guard

Validate before calling

if (parser.currentToken() != XContentParser.Token.START_ARRAY) {
  // materialize a single value as a one-element list, or fail fast
  throw new IllegalStateException("expected array, got " + parser.currentToken());
}

Type guard

boolean isAtArrayStart(XContentParser p) {
  XContentParser.Token t = p.currentToken();
  if (t == XContentParser.Token.FIELD_NAME) {
    try { return p.nextToken() == XContentParser.Token.START_ARRAY; }
    catch (IOException e) { return false; }
  }
  return t == XContentParser.Token.START_ARRAY;
}

Prevention

When it happens

Trigger: Calling XContentParser.list() while positioned on a VALUE_STRING, VALUE_NUMBER, START_OBJECT, END_OBJECT, or null token. Reached when application code assumes a field is an array but the JSON contains a single scalar value, or when parsing logic advances the parser incorrectly before the list read.

Common situations: Documents where a field is sometimes a single value and sometimes an array (JSON's single-vs-array ambiguity). Hand-written XContent parsing code that mishandles token positioning. Custom ingest/rest handlers reading configuration that was serialized as an object instead of an array. Migration of mappings where the field shape changed.

Understand the failure class

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/bb6e2929f89973b8. Report an issue: GitHub.