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

  1. Balance the JSON literal — close every '[' with ']' and every '{' with '}': "$[?(@.a in ['x','y'])]"
  2. Wrap string elements correctly with single quotes inside the JSON literal and verify nesting: "$[?(@.a == {'k':'v'})]"
  3. Validate the predicate with Filter.compile() in a test; the exception message prints the whole filter so you can count the brackets
  4. 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

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


AI-assisted analysis of json-path/JsonPath@62a4c9f0f6 (2026-09-11). Data as JSON: /api/errors/2c7f5e7535af5856. Report an issue: GitHub.