json-path/JsonPath · error · IllegalArgumentException

No type info in TypeRef

Error message

No type info in TypeRef

What it means

TypeRef<T> captures a generic type via a superclass reflection trick that only works when the subclass supplies actual type arguments. Instantiating the raw TypeRef (or new TypeRef() {} with no parameterization) makes getGenericSuperclass() return the plain Class TypeRef, and an IllegalArgumentException 'No type info in TypeRef' is thrown from the constructor.

Source

Thrown at json-path/src/main/java/com/jayway/jsonpath/TypeRef.java:37


/**
 * Used to specify generic type information in {@link com.jayway.jsonpath.ReadContext}
 *
 * <code>
 *       TypeRef ref = new TypeRef<List<Integer>>() { };
 * </code>
 *
 * @param <T>
 */
public abstract class TypeRef<T> implements Comparable<TypeRef<T>> {
    protected final Type type;
    
    protected TypeRef()
    {
        Type superClass = getClass().getGenericSuperclass();
        if (superClass instanceof Class<?>) {
            throw new IllegalArgumentException("No type info in TypeRef");
        }
        type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public Type getType() { return type; }
    
    /**
     * The only reason we define this method (and require implementation
     * of <code>Comparable</code>) is to prevent constructing a
     * reference without type information.
     */
    @Override
    public int compareTo(TypeRef<T> o) {
        return 0;
    }
}

View on GitHub (pinned to 62a4c9f0f6)

Solutions

  1. Always subclass with an explicit type argument: new TypeRef<List<String>>() {}.
  2. If the target type is a simple class, pass the Class directly (read(path, Class)) instead of TypeRef.
  3. Add a compile-time check or unit test exercising the TypeRef read so raw usage fails fast in review.

Example fix

// before
List<Map<String, Object>> list = doc.read("$.items", new TypeRef() {});
// after
List<Map<String, Object>> list = doc.read("$.items", new TypeRef<List<Map<String, Object>>>() {});
Defensive patterns

Strategy: type-guard

Validate before calling

// compile-time: declare the anonymous subclass with an explicit type argument
TypeRef<List<Map<String, Object>>> ref = new TypeRef<List<Map<String, Object>>>() {};

Type guard

static <T> TypeRef<T> typedRef(TypeRef<T> ref) { return Objects.requireNonNull(ref); } // always instantiate as new TypeRef<Concrete>() {}

Try / catch

try {
    return doc.read(path, typeRef);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("No type info in TypeRef")) {
        throw new IllegalStateException("Pass new TypeRef<ConcreteType>() {} with a type argument", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: new TypeRef() {} or new TypeRef() — anonymous/inline subclass without a type parameter, e.g. passing it to JsonPath.parse(...).read("$", new TypeRef(){}) instead of new TypeRef<List<Map<String,Object>>>(){}.

Common situations: Migrating from Class-based reads to TypeRef-based generic reads and forgetting the diamond/type argument; IDE auto-completing 'new TypeRef(){}' without filling in the type.

Related errors


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