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
- Inspect the actual token reported in the message and reshape the input so each array element is a scalar, null, or object.
- 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.
- 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
- Match the itemParser's expectations to the array's actual element shape before calling parseArray.
- Ensure custom ContextParsers return with the cursor positioned after their element so the loop invariant holds.
- Fuzz-test parsers with nested-array inputs to catch shape mismatches in CI.
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
- Input does not start with Smile format header
- [{name}] failed to parse object
- [{name}] failed to parse field [{field}]
- Required [{fields}]
- failed to build [{name}] after last required field arrived
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/0099c62e97ad5b0f.
Report an issue: GitHub.