json-path/JsonPath · error · InvalidPathException

Expected end of filter expression instead of: %s

Error message

Expected end of filter expression instead of: %s

What it means

FilterCompiler.compile() parses the filter expression and then skips blanks; if any characters remain within the '[?(...)]' envelope, the expression is malformed and this InvalidPathException is thrown, naming the leftover text. It means the filter body contained extra tokens after a complete logical expression, e.g. '[?(@.a == 1) @.b]'.

Source

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

        filter.incrementPosition(1);
        filter.decrementEndPosition(1);
        filter.trim();
        if (!filter.currentCharIs('?')) {
            throw new InvalidPathException("Filter must start with '[?' and end with ']'. " + filterString);
        }
        filter.incrementPosition(1);
        filter.trim();
        if (!filter.currentCharIs('(') || !filter.lastCharIs(')')) {
            throw new InvalidPathException("Filter must start with '[?(' and end with ')]'. " + filterString);
        }
    }

    public Predicate compile() {
        try {
             final ExpressionNode result = readLogicalOR();
             filter.skipBlanks();
             if (filter.inBounds()) {
                 throw new InvalidPathException(String.format("Expected end of filter expression instead of: %s",
                         filter.subSequence(filter.position(), filter.length())));
             }

             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()) {

View on GitHub (pinned to 62a4c9f0f6)

Solutions

  1. Inspect the leftover text in the message — it points at exactly where the valid expression ends
  2. Combine predicates with Filter.and(Filter, Filter) or Criteria instead of concatenating '[?...] ' strings
  3. Check for early-closing characters (a stray ')' or quote) that end the expression prematurely
  4. Split the path into a base path plus a single '[?(...)]' filter; only one filter per bracket pair is allowed

Example fix

// before
JsonPath.compile("$..book[?(@.a == 1) @.b == 2]");
// after
JsonPath.compile("$..book[?(@.a == 1 && @.b == 2)]");
Defensive patterns

Strategy: validation

Validate before calling

static boolean singleFilterExpression(String path) {
    return path != null && path.indexOf("[?") == path.lastIndexOf("[?"); // one filter per path segment string
}

Try / catch

try {
    return JsonPath.compile(path);
} catch (InvalidPathException e) {
    if (e.getMessage().startsWith("Expected end of filter expression")) {
        throw new IllegalArgumentException("Trailing tokens after filter expression in: " + path, e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Passing a filter whose inner expression has trailing junk after a parseable expression — extra quotes, an unmatched operand like '[?(@.a == 1 &&)]', duplicated predicates, or concatenated filters '[?(@.a)][?(@.b)]' passed in one string.

Common situations: Merging multiple filter predicates by naive string concatenation instead of using Filter.and()/Filter.filter(); typos leaving stray characters; a quote or bracket closing early so the parser stops mid-string.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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