json-path/JsonPath · error · InvalidPathException
String not closed. Expected
Error message
String not closed. Expected
What it means
Thrown by FilterCompiler.readJsonLiteral when a JSON literal starting with an open bracket '{' or '[' has no matching closing character anywhere in the remaining filter string. indexOfMatchingCloseChar returns -1, so the compiler cannot extract a balanced JSON fragment and raises 'String not closed. Expected ' in <filter>'. It means an inline JSON literal in the predicate is unbalanced.
Source
Thrown at json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java:267
filter.incrementPosition(nullValue.length());
return ValueNode.createNullNode();
}
}
throw new InvalidPathException("Expected <null> value");
}
private JsonNode readJsonLiteral(){
int begin = filter.position();
char openChar = filter.currentChar();
assert openChar == OPEN_ARRAY || openChar == OPEN_OBJECT;
char closeChar = openChar == OPEN_ARRAY ? CLOSE_ARRAY : CLOSE_OBJECT;
int closingIndex = filter.indexOfMatchingCloseChar(filter.position(), openChar, closeChar, true, false);
if (closingIndex == -1) {
throw new InvalidPathException("String not closed. Expected " + SINGLE_QUOTE + " in " + filter);
} else {
filter.setPosition(closingIndex + 1);
}
CharSequence json = filter.subSequence(begin, filter.position());
logger.trace("JsonLiteral from {} to {} -> [{}]", begin, filter.position(), json);
return ValueNode.createJsonNode(json);
}
private int endOfFlags(int position) {
int endIndex = position;
char[] currentChar = new char[1];
while (filter.inBounds(endIndex)) {
currentChar[0] = filter.charAt(endIndex);
if (PatternFlag.parseFlags(currentChar) > 0) {
endIndex++;
continue;
}View on GitHub (pinned to 62a4c9f0f6)
Solutions
- Balance the JSON literal — close every '[' with ']' and every '{' with '}': "$[?(@.a in ['x','y'])]"
- Wrap string elements correctly with single quotes inside the JSON literal and verify nesting: "$[?(@.a == {'k':'v'})]"
- Validate the predicate with Filter.compile() in a test; the exception message prints the whole filter so you can count the brackets
- Build JSON literals with a serializer (e.g. JSONObject.toString()) instead of string concatenation to guarantee balance
Example fix
// before String path = "$[?(@.tags in ['a','b)]"; // after String path = "$[?(@.tags in ['a','b'])]";
Defensive patterns
Strategy: validation
Validate before calling
// Check bracket balance of inline JSON literals before compiling
boolean jsonLiteralClosed(String filter) {
int depth = 0; boolean inStr = false; char prev = 0;
for (char c : filter.toCharArray()) {
if (c == '\'' && prev != '\\') inStr = !inStr;
else if (!inStr) {
if (c == '[' || c == '{') depth++;
if (c == ']' || c == '}') depth--;
if (depth < 0) return false;
}
prev = c;
}
return depth == 0 && !inStr;
} Type guard
static boolean isBalancedPredicate(String filter) {
long open = filter.chars().filter(c -> c == '[' || c == '{').count();
long close = filter.chars().filter(c -> c == ']' || c == '}').count();
return open == close && jsonLiteralClosed(filter);
} Try / catch
try {
List<Map<String, Object>> res = JsonPath.parse(json).read(path);
} catch (InvalidPathException e) {
if (e.getMessage().startsWith("String not closed")) {
throw new IllegalArgumentException("Unbalanced JSON literal (missing ] or }) in filter: " + path, e);
}
throw e;
} Prevention
- Build inline JSON literals with a JSON serializer instead of hand-written strings
- Count brackets/quotes after every manual edit to a filter expression
- Escape single quotes inside JSON literals when needed
- Validate filters with Filter.compile() in CI so unbalanced literals fail at build time
When it happens
Trigger: A filter with an inline JSON object/array literal missing its close, e.g. "$[?(@.a in ['x','y)]" or "$[?(@.a == {'k':1)]" — the scan for the matching ']' or '}' never finds one; hit via Filter.compile or path read.
Common situations: Hand-edited predicates where a quote or bracket got deleted; nested quotes inside the JSON literal confusing manual editing; generating paths programmatically with unescaped characters; very long filters where the imbalance is hard to see.
Related errors
- Unexpected character: %c
- Expected boolean literal
- Expected logical operator
- Expected <null> value
- Expected wildcard token to end with ']' on position
AI-assisted analysis of json-path/JsonPath@62a4c9f0f6 (2026-09-11).
Data as JSON: /api/errors/2c7f5e7535af5856.
Report an issue: GitHub.