{"record":{"id":"0099c62e97ad5b0f","repo":"elastic/elasticsearch","slug":"expected-value-but-got-token","errorCode":null,"errorMessage":"expected value but got [{token}]","messagePattern":"expected value but got \\[(.+?)\\]","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"libs/x-content/src/main/java/org/elasticsearch/xcontent/AbstractObjectParser.java","lineNumber":445,"sourceCode":"     * @param exclusiveSet a set of field names, at most one of which must appear\n     */\n    public abstract void declareExclusiveFieldSet(String... exclusiveSet);\n\n    public static <T, Context> List<T> parseArray(XContentParser parser, Context context, ContextParser<Context, T> itemParser)\n        throws IOException {\n        final XContentParser.Token currentToken = parser.currentToken();\n        if (currentToken.isValue()\n            || currentToken == XContentParser.Token.VALUE_NULL\n            || currentToken == XContentParser.Token.START_OBJECT) {\n            return Collections.singletonList(itemParser.parse(parser, context)); // single value\n        }\n        final List<T> list = new ArrayList<>();\n        XContentParser.Token token;\n        while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {\n            if (token.isValue() || token == XContentParser.Token.VALUE_NULL || token == XContentParser.Token.START_OBJECT) {\n                list.add(itemParser.parse(parser, context));\n            } else {\n                throw new IllegalStateException(\"expected value but got [\" + token + \"]\");\n            }\n        }\n        return list;\n    }\n}\n","sourceCodeStart":427,"sourceCodeEnd":451,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/x-content/src/main/java/org/elasticsearch/xcontent/AbstractObjectParser.java#L427-L451","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: input is [[1,2],[3,4]] but parser expects [1,2,3,4]\nList<Integer> vals = AbstractObjectParser.parseArray(parser, ctx, (c, p) -> p.intValue());\n\n// after: flatten the source, or handle nested arrays explicitly\nList<Integer> vals = new ArrayList<>();\nwhile (parser.nextToken() != Token.END_ARRAY) {\n    AbstractObjectParser.parseArray(parser, ctx, (c, p) -> { vals.add(p.intValue()); return null; });\n}","handlingStrategy":"validation","validationCode":"Token t = parser.currentToken();\nboolean ok = t == Token.START_ARRAY;\n// before iterating: confirm elements are scalars/objects, not nested arrays\nif (ok && parser.nextToken() == Token.START_ARRAY) {\n    // nested array — do not use parseArray, handle recursively\n}","typeGuard":null,"tryCatchPattern":"try {\n    List<T> items = AbstractObjectParser.parseArray(parser, ctx, itemParser);\n} catch (IllegalStateException e) {\n    if (e.getMessage().startsWith(\"expected value but got\")) {\n        // reshape input or switch to a recursive array reader\n    }\n}","preventionTips":["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."],"tags":["xcontent","object-parser","array","deserialization"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}