json-path/JsonPath · error · InvalidPathException

Expected string node

Error message

Expected string node

What it means

In JsonPath, ValueNode is a sealed-style base type whose default asStringNode() implementation throws InvalidPathException("Expected string node"). It is thrown when filter evaluation (evaluate/getInput) calls asStringNode() on a node that is not actually a StringNode — e.g. a NumberNode, BooleanNode, or JsonNode produced by evaluating a path on the wrong document shape. It signals a type mismatch between what the filter operator expected (a string operand) and what the JSON document actually contained.

Source

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

    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");
    }

    public boolean isBooleanNode() {
        return false;
    }

    public BooleanNode asBooleanNode() {
        throw new InvalidPathException("Expected boolean node");
    }

    public boolean isJsonNode() {
        return false;
    }

    public JsonNode asJsonNode() {
        throw new InvalidPathException("Expected json node");
    }

View on GitHub (pinned to 62a4c9f0f6)

Solutions

  1. Change the filter to match the actual JSON type, e.g. compare numbers without quotes: $.items[?(@.price == 0)] instead of == '0'
  2. Check for null/non-string values in the document and normalize them before filtering, or guard with a nested predicate like $.items[?(@.name && @.name == 'x')]
  3. Cast the value in the document (coerce numbers to strings at serialization time) so operands are consistent
  4. Catch InvalidPathException around the evaluation and treat the mismatching item as a non-match

Example fix

// before
List<Map<String,Object>> hits = JsonPath.parse(json).read("$.items[?(@.code == '123')]"); // code is a number
// after
List<Map<String,Object>> hits = JsonPath.parse(json).read("$.items[?(@.code == 123)]");
Defensive patterns

Strategy: type-guard

Validate before calling

Object v = JsonPath.parse(json).read("$.item.price");
if (v == null || !(v instanceof String)) throw new IllegalArgumentException("price must be a string for this filter");

Type guard

static boolean isStringOperand(Object o) { return o instanceof String; }

Try / catch

try {
    return JsonPath.parse(json).read("$.items[?(@.code == '123')]");
} catch (InvalidPathException e) {
    // operand type mismatch: treat as non-match or re-run with corrected literal
    return Collections.emptyList();
}

Prevention

When it happens

Trigger: Using string comparison operators (==, =~, in, like, etc.) in a JSONPath filter where the evaluated operand is not a string, e.g. $.items[?(@.price == 'free')] where price is a number, or a path operand resolving to null/boolean instead of a string. Also calling StringNode.asStringNode() via evaluate/getInput on a subclass node.

Common situations: Filter predicates written against documents where field types vary (one record has "name": "x", another has name: null); comparing numeric fields to quoted string literals; migrating documents from loosely typed sources where an expected string field arrives as a number or object.

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