XmlPath: a negative list index on a hyphenated element name throws a misleading parameter "..." was used but not defined error

Author: haskiindahouseCreated Jul 21, 2026Updated Jul 21, 2026

Description

XmlPath auto-escapes hyphenated element names so they need no manual quoting (the feature that closed #74/#105/#564), and it supports negative from-the-end list indexes such as records.car[-1]. Each works on its own. Combining them throws.

XML: <root><some-list>one</some-list><some-list>two</some-list><some-list>three</some-list></root>

getString("root.some-list[-1]")   -> IllegalArgumentException:
    The parameter "list" was used but not defined.
    Define parameters using the XmlPath.params(...) function      (should be "three")
getString("root.some-list[-2]")   -> same exception                (should be "two")

The caller never used a parameter and never called params(...). The identifier list is the tail of the element name some-list; the MissingPropertyException handler turns it into a red herring about params(...).

rest-assured version

  • Reproduced on released io.rest-assured:xml-path:5.5.6 (Maven Central) and on master HEAD eeed4998 (xml-path 6.0.2-SNAPSHOT). Present in both.
  • Java 24 (Temurin 24.0.1).

Steps to reproduce

java
import io.restassured.path.xml.XmlPath;

public class Repro {
    static String xml = "<root><some-list>one</some-list><some-list>two</some-list><some-list>three</some-list></root>";
    static String get(String p) {
        try { return new XmlPath(xml).getString(p); }
        catch (Throwable t) { return "EXC " + t.getClass().getSimpleName() + ": " + t.getMessage(); }
    }
    public static void main(String[] a) {
        System.out.println("some-list[-1]    = " + get("root.some-list[-1]"));   // EXC (should be three)
        System.out.println("some-list[-2]    = " + get("root.some-list[-2]"));   // EXC (should be two)
        System.out.println("some-list[0]     = " + get("root.some-list[0]"));    // one  (positive: OK)
        System.out.println("'some-list'[-1]  = " + get("root.'some-list'[-1]")); // three (manual-quote workaround)
    }
}

Output. Each feature works in isolation; the hyphen plus negative-index intersection throws:

some-list[-1]    = EXC IllegalArgumentException: The parameter "list" was used but not defined. ...
some-list[-2]    = EXC IllegalArgumentException: The parameter "list" was used but not defined. ...
some-list[0]     = one
'some-list'[-1]  = three

Control with a non-hyphenated name: <root><item>one</item>...<item>three</item></root> gives root.item[-1] = "three", so the negative index itself works. Workaround: quote the name by hand, root.'some-list'[-1].

Expected

root.some-list[-1] = "three", root.some-list[-2] = "two" (matching /root/some-list[last()] / [last()-1] from a standard XPath oracle, and consistent with both features being documented and tested).

Actual

IllegalArgumentException: The parameter "list" was used but not defined. A documented feature combination throws, and the message blames a params(...) parameter the caller never used.

Root cause

rest-assured-common/src/main/groovy/io/restassured/internal/common/assertion/AssertionSupport.groovy:76, the hyphen() escaper's guard:

groovy
boolean shouldEscape(String pathFragment) {
  !pathFragment.startsWith("'") && !pathFragment.endsWith("'") && pathFragment.contains('-') &&
  !pathFragment.contains('[-') &&                                   // <-- this clause
  !containsAny(pathFragment, [closureStartFragment, closureEndFragment, listGetterFragment])
}

For the fragment some-list[-1]: it contains -, so the escaper would rewrite it to 'some-list'[-1], except it also contains the substring [- (from the [-1] index), so shouldEscape returns false and escaping is skipped. The unescaped some-list[-1] reaches the Groovy GPath evaluator, which parses some-list as the subtraction some - list; list is an undefined binding, producing MissingPropertyException(property = "list"), which XMLAssertion.groovy reformats into the misleading params(...) message. The escaper's own escape() method splits before [ and would produce the correct 'some-list'[-1]; the shouldEscape gate alone opts out.

Suggested fix

The [- clause keeps JsonPath's bare leading negative index ([-1].email) out of the escaper, so keep it in spirit and narrow it to fire only when the hyphen sits in the element-name portion before [:

groovy
def nameHead = org.apache.commons.lang3.StringUtils.substringBefore(pathFragment, '[')
// ... && nameHead.contains('-') && !containsAny(...)   // replaces `contains('-') && !contains('[-')`

This escapes some-list[-1] to 'some-list'[-1] (returning three) while leaving bare [-1] and non-hyphen item[-1] to GPath's native negative-index handling. Add a regression test for a hyphenated name with a negative index.


Found via property-based & differential bug-hunting, part of an effort to scale PBT (DepTyCheck-based) testing across the OSS ecosystem.

If this is intended / by-design: I'm really sorry, please just close it — no need to flag or ban me. I'm trying to scale property-based testing across the whole ecosystem and my publishing agents may have gotten this one wrong. I read every issue and follow up on each.

Source: rest-assured/rest-assured