{"record":{"id":"2c7f5e7535af5856","repo":"json-path/JsonPath","slug":"string-not-closed-expected","errorCode":null,"errorMessage":"String not closed. Expected ","messagePattern":"String not closed\\. Expected ","errorType":"exception","errorClass":"InvalidPathException","httpStatus":null,"severity":"error","filePath":"json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java","lineNumber":267,"sourceCode":"                filter.incrementPosition(nullValue.length());\n                return ValueNode.createNullNode();\n            }\n        }\n        throw new InvalidPathException(\"Expected <null> value\");\n    }\n\n    private JsonNode readJsonLiteral(){\n        int begin = filter.position();\n\n        char openChar = filter.currentChar();\n\n        assert openChar == OPEN_ARRAY || openChar == OPEN_OBJECT;\n\n        char closeChar = openChar == OPEN_ARRAY ? CLOSE_ARRAY : CLOSE_OBJECT;\n\n        int closingIndex = filter.indexOfMatchingCloseChar(filter.position(), openChar, closeChar, true, false);\n        if (closingIndex == -1) {\n            throw new InvalidPathException(\"String not closed. Expected \" + SINGLE_QUOTE + \" in \" + filter);\n        } else {\n            filter.setPosition(closingIndex + 1);\n        }\n        CharSequence json = filter.subSequence(begin, filter.position());\n        logger.trace(\"JsonLiteral from {} to {} -> [{}]\", begin, filter.position(), json);\n        return ValueNode.createJsonNode(json);\n\n    }\n\n    private int endOfFlags(int position) {\n        int endIndex = position;\n        char[] currentChar = new char[1];\n        while (filter.inBounds(endIndex)) {\n            currentChar[0] = filter.charAt(endIndex);\n            if (PatternFlag.parseFlags(currentChar) > 0) {\n                endIndex++;\n                continue;\n            }","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/json-path/JsonPath/blob/62a4c9f0f65ba3f625aa0867d64c528ba72d09ec/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java#L249-L285","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nString path = \"$[?(@.tags in ['a','b)]\";\n// after\nString path = \"$[?(@.tags in ['a','b'])]\";","handlingStrategy":"validation","validationCode":"// Check bracket balance of inline JSON literals before compiling\nboolean jsonLiteralClosed(String filter) {\n    int depth = 0; boolean inStr = false; char prev = 0;\n    for (char c : filter.toCharArray()) {\n        if (c == '\\'' && prev != '\\\\') inStr = !inStr;\n        else if (!inStr) {\n            if (c == '[' || c == '{') depth++;\n            if (c == ']' || c == '}') depth--;\n            if (depth < 0) return false;\n        }\n        prev = c;\n    }\n    return depth == 0 && !inStr;\n}","typeGuard":"static boolean isBalancedPredicate(String filter) {\n    long open = filter.chars().filter(c -> c == '[' || c == '{').count();\n    long close = filter.chars().filter(c -> c == ']' || c == '}').count();\n    return open == close && jsonLiteralClosed(filter);\n}","tryCatchPattern":"try {\n    List<Map<String, Object>> res = JsonPath.parse(json).read(path);\n} catch (InvalidPathException e) {\n    if (e.getMessage().startsWith(\"String not closed\")) {\n        throw new IllegalArgumentException(\"Unbalanced JSON literal (missing ] or }) in filter: \" + path, e);\n    }\n    throw e;\n}","preventionTips":["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"],"tags":["json-path","filter-compiler","json-literal","unclosed-bracket","path-syntax"],"backgroundTag":"invalid-path-expression","analyzedSha":"62a4c9f0f65ba3f625aa0867d64c528ba72d09ec","analyzedAt":"2026-09-11T11:36:00.448Z","contentChangedAt":"2026-09-11T11:36:00.448Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}