json-path/JsonPath · error · InvalidPathException

Expected boolean literal

Error message

Expected boolean literal

What it means

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.

Source

Thrown at json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java:213

            return new RelationalExpressionNode(left, operator, right);
        }
        catch (InvalidPathException exc) {
            filter.setPosition(savepoint);
        }

        PathNode pathNode = left.asPathNode();
        left = pathNode.asExistsCheck(pathNode.shouldExists());
        RelationalOperator operator = RelationalOperator.EXISTS;
        ValueNode right = left.asPathNode().shouldExists() ? ValueNodes.TRUE : ValueNodes.FALSE;
        return new RelationalExpressionNode(left, operator, right);
    }

    private LogicalOperator readLogicalOperator(){
        int begin = filter.skipBlanks().position();
        int end = begin+1;

        if(!filter.inBounds(end)){
            throw new InvalidPathException("Expected boolean literal");
        }
        CharSequence logicalOperator = filter.subSequence(begin, end+1);
        if(!logicalOperator.equals("||") && !logicalOperator.equals("&&")){
            throw new InvalidPathException("Expected logical operator");
        }
        filter.incrementPosition(logicalOperator.length());
        logger.trace("LogicalOperator from {} to {} -> [{}]", begin, end, logicalOperator);

        return LogicalOperator.fromString(logicalOperator.toString());
    }

    private RelationalOperator readRelationalOperator() {
        int begin = filter.skipBlanks().position();

        if(isRelationalOperatorChar(filter.currentChar())){
            while (filter.inBounds() && isRelationalOperatorChar(filter.currentChar())) {
                filter.incrementPosition(1);
            }

View on GitHub (pinned to 62a4c9f0f6)

Solutions

  1. Join multiple comparisons with '&&' or '||' (two characters), never single '&' or '|': "$[?(@.a == 1 && @.b == 2)]"
  2. Check that the expression is not truncated — ensure there is a complete operator plus right operand after each comparison
  3. Wrap each comparison in parentheses to make operator grouping explicit: "$[?((@.a == 1) && (@.b == 2))]"
  4. Print the compiled predicate string before calling read() to spot missing/duplicated characters

Example fix

// before
String path = "$[?(@.price < 10 & @.inStock == true)]";
// after
String path = "$[?(@.price < 10 && @.inStock == true)]";
Defensive patterns

Strategy: validation

Validate before calling

// Ensure no truncated/trailing single & or | remains
boolean hasCompleteLogicalOps(String predicate) {
    return !predicate.matches(".*[^&|][&|][^&|].*") && !predicate.matches(".*[&|]\\s*$");
}
// "@.a == 1 && @.b == 2" -> true; "@.a == 1 &" -> false

Type guard

static boolean usesOnlyCompleteOperators(String filter) {
    return !java.util.regex.Pattern.compile("(^|[^&|])([&|])($|[^&|])").matcher(filter).find();
}

Try / catch

try {
    List<Map<String, Object>> hits = JsonPath.parse(json).read(path);
} catch (InvalidPathException e) {
    if (e.getMessage().contains("Expected boolean literal")) {
        throw new IllegalArgumentException("Use && or || (two chars) between comparisons in: " + path);
    }
    throw e;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


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