{"record":{"id":"15390b626c20e672","repo":"json-path/JsonPath","slug":"expected-boolean-literal","errorCode":null,"errorMessage":"Expected boolean literal","messagePattern":"Expected boolean literal","errorType":"exception","errorClass":"InvalidPathException","httpStatus":null,"severity":"error","filePath":"json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java","lineNumber":213,"sourceCode":"            return new RelationalExpressionNode(left, operator, right);\n        }\n        catch (InvalidPathException exc) {\n            filter.setPosition(savepoint);\n        }\n\n        PathNode pathNode = left.asPathNode();\n        left = pathNode.asExistsCheck(pathNode.shouldExists());\n        RelationalOperator operator = RelationalOperator.EXISTS;\n        ValueNode right = left.asPathNode().shouldExists() ? ValueNodes.TRUE : ValueNodes.FALSE;\n        return new RelationalExpressionNode(left, operator, right);\n    }\n\n    private LogicalOperator readLogicalOperator(){\n        int begin = filter.skipBlanks().position();\n        int end = begin+1;\n\n        if(!filter.inBounds(end)){\n            throw new InvalidPathException(\"Expected boolean literal\");\n        }\n        CharSequence logicalOperator = filter.subSequence(begin, end+1);\n        if(!logicalOperator.equals(\"||\") && !logicalOperator.equals(\"&&\")){\n            throw new InvalidPathException(\"Expected logical operator\");\n        }\n        filter.incrementPosition(logicalOperator.length());\n        logger.trace(\"LogicalOperator from {} to {} -> [{}]\", begin, end, logicalOperator);\n\n        return LogicalOperator.fromString(logicalOperator.toString());\n    }\n\n    private RelationalOperator readRelationalOperator() {\n        int begin = filter.skipBlanks().position();\n\n        if(isRelationalOperatorChar(filter.currentChar())){\n            while (filter.inBounds() && isRelationalOperatorChar(filter.currentChar())) {\n                filter.incrementPosition(1);\n            }","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/json-path/JsonPath/blob/62a4c9f0f65ba3f625aa0867d64c528ba72d09ec/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java#L195-L231","documentation":"Thrown by FilterCompiler.readLogicalOperator when, after parsing the left side of a predicate, the filter ends before a two-character boolean operator can be read. The compiler checks inBounds(end) for the two-char token ('&&' or '||'); if the expression stops right after the left operand (or only one char of the operator remains), it reports 'Expected boolean literal'. It signals a truncated or missing logical connective between two comparisons.","triggerScenarios":"A filter where a second comparison follows without a complete operator: \"$[?(@.a == 1 & @.b == 2)]\" (single '&'), or \"$[?(@.a == 1|)]\"; also expressions ending abruptly after the left operand during JsonPath.parse(...).read(path).","commonSituations":"Using single '&' or '|' instead of '&&'/'||' (SQL or shell habits); copying an expression and losing one character; dynamic path builders that join conditions with the wrong separator; expression truncated by template interpolation.","solutions":["Join multiple comparisons with '&&' or '||' (two characters), never single '&' or '|': \"$[?(@.a == 1 && @.b == 2)]\"","Check that the expression is not truncated — ensure there is a complete operator plus right operand after each comparison","Wrap each comparison in parentheses to make operator grouping explicit: \"$[?((@.a == 1) && (@.b == 2))]\"","Print the compiled predicate string before calling read() to spot missing/duplicated characters"],"exampleFix":"// before\nString path = \"$[?(@.price < 10 & @.inStock == true)]\";\n// after\nString path = \"$[?(@.price < 10 && @.inStock == true)]\";","handlingStrategy":"validation","validationCode":"// Ensure no truncated/trailing single & or | remains\nboolean hasCompleteLogicalOps(String predicate) {\n    return !predicate.matches(\".*[^&|][&|][^&|].*\") && !predicate.matches(\".*[&|]\\\\s*$\");\n}\n// \"@.a == 1 && @.b == 2\" -> true; \"@.a == 1 &\" -> false","typeGuard":"static boolean usesOnlyCompleteOperators(String filter) {\n    return !java.util.regex.Pattern.compile(\"(^|[^&|])([&|])($|[^&|])\").matcher(filter).find();\n}","tryCatchPattern":"try {\n    List<Map<String, Object>> hits = JsonPath.parse(json).read(path);\n} catch (InvalidPathException e) {\n    if (e.getMessage().contains(\"Expected boolean literal\")) {\n        throw new IllegalArgumentException(\"Use && or || (two chars) between comparisons in: \" + path);\n    }\n    throw e;\n}","preventionTips":["Always use && / || (doubled) — never single & or |","Join dynamically built conditions with '&&' constants, not interpolated separators","Keep a regression test that compiles each production filter expression","Parenthesize each comparison for readability and safer parsing"],"tags":["json-path","filter-compiler","logical-operator","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"}