json-path/JsonPath · error · InvalidPathException
Expected logical operator
Error message
Expected logical operator
What it means
Thrown by FilterCompiler.readLogicalOperator when the two-character sequence at the current position is neither '||' nor '&&'. The compiler reads a two-char token and, if it is not a recognized logical operator, raises 'Expected logical operator'. It means an unexpected token (or an unsupported operator spelling) sits where a boolean connective is required between predicate operands.
Source
Thrown at json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java:217
}
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);
}
} else {
while (filter.inBounds() && filter.currentChar() != SPACE) {
filter.incrementPosition(1);
}View on GitHub (pinned to 62a4c9f0f6)
Solutions
- Replace word operators with the symbolic form: use '&&' for AND and '||' for OR — JsonPath filters do not accept 'and'/'or'
- Combine comparisons into a single condition when possible: "$[?(@.a > 1 && @.a < 10)]"
- Escape or move stray characters out of the predicate — verify every token between comparisons is exactly '&&' or '||'
- Test the predicate with Filter.compile() to isolate the failing position
Example fix
// before String path = "$[?(@.a == 1 or @.b == 2)]"; // after String path = "$[?(@.a == 1 || @.b == 2)]";
Defensive patterns
Strategy: validation
Validate before calling
// Reject word operators before compiling
boolean validOperators(String predicate) {
return !predicate.toLowerCase().matches(".*\\b(and|or)\\b.*");
}
// validOperators("$[?(@.a == 1 and @.b == 2)]") -> false Type guard
static boolean containsWordOperator(String filter) {
return java.util.regex.Pattern.compile("\\b(and|or|AND|OR)\\b").matcher(filter).find();
} Try / catch
try {
Filter f = Filter.compile(predicate);
} catch (InvalidPathException e) {
if (e.getMessage().contains("Expected logical operator")) {
String fixed = predicate.replaceAll("(?i)\\band\\b", "&&").replaceAll("(?i)\\bor\\b", "||");
return Filter.compile(fixed);
}
throw e;
} Prevention
- JsonPath only accepts symbolic && and || — never 'and'/'or'/'AND'/'OR'
- When migrating expressions from SQL/SpEL, mechanically replace word operators first
- Lint filter strings with a regex for forbidden word operators at input boundaries
- Prefer the Filter.Builder API (filter(where(...).and(...))) which avoids raw operator strings entirely
When it happens
Trigger: A filter containing an operator other than &&/|| between comparisons, e.g. "and"/"or" as words ("$[?(@.a == 1 and @.b == 2)]"), 'AND', or a stray single character; hit via Filter.compile or path read with an inline filter predicate.
Common situations: Translating expressions from other query languages (SQL 'AND'/'OR') into JsonPath; accidentally embedding comparison operators like '==' where a connective belongs; typos such as '& &' or '| |'.
Related errors
- Expected boolean literal
- Unexpected character: %c
- Expected <null> value
- String not closed. Expected
- Expected wildcard token to end with ']' on position
AI-assisted analysis of json-path/JsonPath@62a4c9f0f6 (2026-09-11).
Data as JSON: /api/errors/9f609f5a7763d709.
Report an issue: GitHub.