json-path/JsonPath · error · InvalidPathException
Unexpected character: %c
Error message
Unexpected character: %c
What it means
This InvalidPathException is thrown by JsonPath's FilterCompiler while parsing an inline filter predicate. Inside readValueNode, when the current token is the NOT operator ('!'), the compiler expects a path starting with the document context char '@' or the evaluation context char '#' to follow; any other character (e.g. '!x' or '! 'a' == 'b') is unrecoverable. It means the negation operator in the filter is not immediately followed by a valid path operand.
Source
Thrown at json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java:102
return result;
} catch (InvalidPathException e){
throw e;
} catch (Exception e) {
throw new InvalidPathException("Failed to parse filter: " + filter + ", error on position: " + filter.position() + ", char: " + filter.currentChar());
}
}
private ValueNode readValueNode() {
switch (filter.skipBlanks().currentChar()) {
case DOC_CONTEXT : return readPath();
case EVAL_CONTEXT : return readPath();
case NOT:
filter.incrementPosition(1);
switch (filter.skipBlanks().currentChar()) {
case DOC_CONTEXT : return readPath();
case EVAL_CONTEXT : return readPath();
default: throw new InvalidPathException(String.format("Unexpected character: %c", NOT));
}
default : return readLiteral();
}
}
private ValueNode readLiteral(){
switch (filter.skipBlanks().currentChar()){
case SINGLE_QUOTE: return readStringLiteral(SINGLE_QUOTE);
case DOUBLE_QUOTE: return readStringLiteral(DOUBLE_QUOTE);
case TRUE: return readBooleanLiteral();
case FALSE: return readBooleanLiteral();
case MINUS: return readNumberLiteral();
case NULL: return readNullLiteral();
case OPEN_OBJECT: return readJsonLiteral();
case OPEN_ARRAY: return readJsonLiteral();
case PATTERN: return readPattern();
default: return readNumberLiteral();
}View on GitHub (pinned to 62a4c9f0f6)
Solutions
- Fix the filter so '!' is directly followed by a path operand: use "$[?(!@.field)]" or "$[?(!(@.field == 'x'))]"
- If you meant inequality, use the '!=' operator instead of '!': "$[?(@.field != 'x')]"
- Wrap negation in parentheses around a comparison: "$[?(!(@.a && @.b))]" rather than negating a literal
- Verify the whole expression compiles with Filter.compile(predicate) in a unit test before deploying
Example fix
// before String path = "$[?(! 'active' == true)]"; // after String path = "$[?(!@.active == true)]";
Defensive patterns
Strategy: validation
Validate before calling
// Validate a negation before compiling
boolean validNegation(String predicate) {
int i = predicate.indexOf('!');
if (i < 0 || i == predicate.length() - 1) return false;
char next = predicate.charAt(i + 1);
return next == '@' || next == '#' || next == '(';
}
// usage: if (!validNegation("$[?(!@.x)]")) throw new IllegalArgumentException("bad filter"); Type guard
static boolean isNegatingPath(String filter) {
java.util.regex.Matcher m = java.util.regex.Pattern.compile("!\\s*[@#(]").matcher(filter);
return m.find();
} Try / catch
try {
Object result = JsonPath.parse(json).read(path);
} catch (InvalidPathException e) {
log.error("Malformed filter predicate, ! must precede @ or # : " + path, e);
throw new IllegalArgumentException("Invalid JSONPath filter: " + path, e);
} Prevention
- Always follow '!' immediately with @, #, or a parenthesized group
- Use '!=' for inequality instead of '! ='
- Unit-test every user-supplied filter string with Filter.compile() before use
When it happens
Trigger: Compiling a filter path where '!' is followed by something that is not '@' or '#', e.g. "$[?(! foo)]", "$[?(! 'a' == 'b')]", or "$[?(!)]" — anywhere Filter.compile / JsonPath.parse(...).read(path) evaluates the predicate.
Common situations: Hand-written filter expressions with a stray space or wrong operand after '!' (users expect '!= value' syntax to be written as '! ='); macro-generated paths that prepend '!' to a literal instead of a path; upgrading JsonPath and an expression that previously slipped through stricter validation now fails.
Related errors
- Expected boolean literal
- Expected logical operator
- 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/3f2ee6b405c2f56d.
Report an issue: GitHub.