apple/pkl · error · IllegalStateException

JavaType token must be parameterized.

Error message

JavaType token must be parameterized.

What it means

JavaType<T> is a super-type-token: the protected no-arg constructor captures T via the concrete subclass's generic superclass. It throws IllegalStateException when the class using it is not a parameterized subclass (raw JavaType or non-generic subclass), because then there is no TypeArgument to capture into the type field.

Source

Thrown at pkl-config-java/src/main/java/org/pkl/config/java/JavaType.java:100

 */
public class JavaType<T> {
  private final Type type;

  /**
   * Constructs a {@code JavaType} using the super-type token idiom.
   *
   * <p>Subclasses must be parameterized with the desired type, for example:
   *
   * <pre>{@code
   * new JavaType<List<@Nullable String>>() {}
   * }</pre>
   *
   * @throws IllegalStateException if this instance is not parameterized
   */
  protected JavaType() {
    var superclass = getClass().getGenericSuperclass();
    if (!(superclass instanceof ParameterizedType parameterizedType)) {
      throw new IllegalStateException("JavaType token must be parameterized.");
    }
    type = parameterizedType.getActualTypeArguments()[0];
  }

  private JavaType(Type type) {
    this.type = type;
  }

  /** Creates a {@code JavaType} for the given type. */
  public static <T> JavaType<T> of(Class<T> type) {
    return new JavaType<>(type);
  }

  /**
   * Creates a {@code JavaType} for the given {@link Type}.
   *
   * <p>Use this method when the target type is already available as a {@link Type}; otherwise,
   * prefer {@link #of(Class)}.

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Instantiate as a parameterized anonymous subclass: new JavaType<List<String>>() {}.
  2. If you already have a java.lang.reflect.Type, use the private/alternative constructor path or mapper APIs that accept a Type directly instead of the token.
  3. Never write raw new JavaType(); always supply the generic argument.

Example fix

// before
JavaType type = new JavaType<>() {}; // raw target, no reified argument
// after
JavaType<Map<String, String>> type = new JavaType<Map<String, String>>() {};
Defensive patterns

Strategy: type-guard

Validate before calling

// ensure the token class carries a reified type argument
static <T> JavaType<T> token(Class<? extends JavaType<T>> impl) {
  return java.lang.reflect.Modifier.isAbstract(impl.getModifiers())
      ? fail("JavaType subclass must be concrete and parameterized")
      : instantiate(impl);
}

Type guard

static boolean isParameterized(JavaType<?> t) {
  return t.getClass().getGenericSuperclass() instanceof ParameterizedType pt
      && pt.getActualTypeArguments()[0] instanceof Class<?>;
}

Try / catch

try {
  JavaType<List<String>> t = new JavaType<List<String>>() {};
  return mapper.map(value, t);
} catch (IllegalStateException e) {
  throw new IllegalArgumentException("Use new JavaType<T>() {} with an explicit type", e);
}

Prevention

When it happens

Trigger: Instantiating JavaType anonymously or as a subclass without a type argument, e.g. new JavaType() {...} instead of new JavaType<Map<String, String>>() {}, or referencing JavaType.class directly as a raw type.

Common situations: Copy-pasting a JavaType snippet but dropping the diamond type argument; using JavaType with a type variable erased at runtime; building a generic helper that instantiates new JavaType<T>() where T is not reified by a subclass.

Related errors


AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08). Data as JSON: /api/errors/4482c3ed83db016f. Report an issue: GitHub.