elastic/elasticsearch · error · IllegalStateException

expected value but got [{token}]

Error message

expected value but got [{token}]

What it means

Thrown by AbstractObjectParser.parseArray while iterating array elements: it only accepts VALUE tokens, VALUE_NULL, or START_OBJECT per element. Any other token (START_ARRAY for nested arrays, FIELD_NAME, END_OBJECT) is rejected as a structural error. This is an IllegalStateException, not an XContentParseException, so it signals the array shape diverged from what the item parser can consume.

Source

Thrown at libs/x-content/src/main/java/org/elasticsearch/xcontent/AbstractObjectParser.java:445

     * @param exclusiveSet a set of field names, at most one of which must appear
     */
    public abstract void declareExclusiveFieldSet(String... exclusiveSet);

    public static <T, Context> List<T> parseArray(XContentParser parser, Context context, ContextParser<Context, T> itemParser)
        throws IOException {
        final XContentParser.Token currentToken = parser.currentToken();
        if (currentToken.isValue()
            || currentToken == XContentParser.Token.VALUE_NULL
            || currentToken == XContentParser.Token.START_OBJECT) {
            return Collections.singletonList(itemParser.parse(parser, context)); // single value
        }
        final List<T> list = new ArrayList<>();
        XContentParser.Token token;
        while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
            if (token.isValue() || token == XContentParser.Token.VALUE_NULL || token == XContentParser.Token.START_OBJECT) {
                list.add(itemParser.parse(parser, context));
            } else {
                throw new IllegalStateException("expected value but got [" + token + "]");
            }
        }
        return list;
    }
}

View on GitHub (pinned to db6a809a66)

Solutions

  1. Inspect the actual token reported in the message and reshape the input so each array element is a scalar, null, or object.
  2. If nested arrays are legitimately expected, switch from parseArray to a custom loop that handles START_ARRAY recursively instead of relying on the shared helper.
  3. Verify the itemParser does not call nextToken() past its element boundary, which would leave the cursor on a FIELD_NAME/END_OBJECT and trip the guard.

Example fix

// before: input is [[1,2],[3,4]] but parser expects [1,2,3,4]
List<Integer> vals = AbstractObjectParser.parseArray(parser, ctx, (c, p) -> p.intValue());

// after: flatten the source, or handle nested arrays explicitly
List<Integer> vals = new ArrayList<>();
while (parser.nextToken() != Token.END_ARRAY) {
    AbstractObjectParser.parseArray(parser, ctx, (c, p) -> { vals.add(p.intValue()); return null; });
}
Defensive patterns

Strategy: validation

Validate before calling

Token t = parser.currentToken();
boolean ok = t == Token.START_ARRAY;
// before iterating: confirm elements are scalars/objects, not nested arrays
if (ok && parser.nextToken() == Token.START_ARRAY) {
    // nested array — do not use parseArray, handle recursively
}

Try / catch

try {
    List<T> items = AbstractObjectParser.parseArray(parser, ctx, itemParser);
} catch (IllegalStateException e) {
    if (e.getMessage().startsWith("expected value but got")) {
        // reshape input or switch to a recursive array reader
    }
}

Prevention

When it happens

Trigger: Feeding a nested array (array-of-arrays) to a parser whose itemParser expects scalar/object elements. Malformed JSON where an object body appears where array elements are expected. A custom ContextParser that advances the cursor inconsistently, leaving the parser positioned on a FIELD_NAME or START_ARRAY inside the loop.

Common situations: Schema drift: a field declared as a flat list starts receiving grouped/nested data after an upstream change. Reusing a parser across mixed shapes. Tests with hand-written JSON that accidentally nests one level too deep.

Related errors


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