json-path/JsonPath · error · InvalidPathException

Filter must start with '[?(' and end with ')]'.

Error message

Filter must start with '[?(' and end with ')]'. 

What it means

After '[?' is accepted, FilterCompiler trims and requires the next character to be '(' and the last character of the trimmed string to be ')'. Filters must be fully parenthesized: '[?(<expression>)]'. Unbalanced or missing parentheses throw this InvalidPathException.

Source

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

        return new CompiledFilter(compiler.compile());
    }

    private FilterCompiler(String filterString) {
        filter = new CharacterIndex(filterString);
        filter.trim();
        if (!filter.currentCharIs('[') || !filter.lastCharIs(']')) {
            throw new InvalidPathException("Filter must start with '[' and end with ']'. " + filterString);
        }
        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());
        }

View on GitHub (pinned to 62a4c9f0f6)

Solutions

  1. Ensure the predicate is wrapped in a balanced parenthesis pair: '[?(' + expr + ')]'
  2. Count parentheses in nested logical expressions, e.g. '[?(@.a && (@.b || @.c))]'
  3. When building dynamically, encapsulate in one helper that always emits the full '[?(...)]' wrapper
  4. Compile the path in a test to catch the truncation early

Example fix

// before
JsonPath.compile("$..book[?@.price < 10]");
// after
JsonPath.compile("$..book[?(@.price < 10)]");
Defensive patterns

Strategy: validation

Validate before calling

static boolean isParenthesizedFilter(String filter) {
    String t = filter == null ? "" : filter.trim();
    return t.startsWith("[?(" ) && t.endsWith(")]");
}

Type guard

boolean balancedParens(String expr) {
    int d = 0;
    for (char c : expr.toCharArray()) { if (c=='(') d++; if (c==')') d--; if (d<0) return false; }
    return d == 0;
}

Try / catch

try {
    return JsonPath.compile(path);
} catch (InvalidPathException e) {
    throw new IllegalArgumentException("Filter must be '[?(...)]', got: " + path, e);
}

Prevention

When it happens

Trigger: A filter like '[?@.price < 10)]' or '[?(@.price < 10]' (missing closing paren or bracket), produced by string concatenation where only one side of the predicate wrapper was added.

Common situations: Dynamic query building where '(' was added but ')]' truncated; copying a partial expression from a log; nested parentheses miscounted so the final ')' is missing.

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/7357b55848522373. Report an issue: GitHub.