json-path/JsonPath · error · InvalidPathException

Expected path node

Error message

Expected path node

What it means

ValueNode.asPathNode() is the default implementation that every non-path ValueNode inherits; only a real PathNode can be converted. When filter code (e.g. pathNode() or the right() operand handling) calls asPathNode() on a node that is actually a literal (string/number/boolean/json) node, the library throws InvalidPathException("Expected path node").

Source

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

public abstract class ValueNode {

    public abstract Class<?> type(Predicate.PredicateContext ctx);

    public boolean isPatternNode() {
        return false;
    }

    public PatternNode asPatternNode() {
        throw new InvalidPathException("Expected regexp node");
    }

    public boolean isPathNode() {
        return false;
    }

    public PathNode asPathNode() {
        throw new InvalidPathException("Expected path node");
    }

    public boolean isNumberNode() {
        return false;
    }

    public NumberNode asNumberNode() {
        throw new InvalidPathException("Expected number node");
    }

    public boolean isStringNode() {
        return false;
    }

    public StringNode asStringNode() {
        throw new InvalidPathException("Expected string node");
    }

View on GitHub (pinned to 62a4c9f0f6)

Solutions

  1. Call isPathNode() to check the node kind before invoking asPathNode(), and handle literal nodes explicitly.
  2. Ensure the operand is an unquoted path expression (e.g. @.price) so the parser produces a PathNode, not a string literal.
  3. When constructing filter nodes programmatically, wrap path expressions in ValueNode.PathNode rather than passing raw literals.
  4. Add a debug log of the node's class (e.g. node.getClass()) at the call site to see which literal type leaked in.

Example fix

// before
ValueNode node = new ValueNode.StringNode("@.price");
node.asPathNode(); // throws

// after
ValueNode node = new ValueNode.PathNode(Path.compile("@.price"));
if (node.isPathNode()) {
    node.asPathNode();
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Java: ensure path operands are unquoted path expressions starting with @ or $
void validatePathOperand(String expr) {
    if (!(expr.startsWith("@.") || expr.startsWith("$"))) {
        throw new IllegalArgumentException("Expected a path operand like @.field, got: " + expr);
    }
}

Type guard

boolean asPathSafe(ValueNode node) {
    return node.isPathNode(); // only then call node.asPathNode()
}

Try / catch

try {
    return node.asPathNode();
} catch (InvalidPathException e) {
    if (e.getMessage().equals("Expected path node")) {
        throw new IllegalStateException("Expected a PathNode, got " + node.getClass().getSimpleName(), e);
    }
    throw e;
}

Prevention

When it happens

Trigger: A filter predicate where one side must resolve to a JSON path (e.g. comparisons of path-to-path or @.a == @.b style nodes) but the operand parsed as a literal value; calling asPathNode() on a ValueNode obtained from parsing a quoted string or number.

Common situations: Dynamically built filter nodes where a raw string was used where a PathNode was required; expressions referencing document fields that got quoted accidentally, turning them into string nodes; custom filter evaluation code assuming both operands are path nodes.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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