apple/pkl · error · IllegalArgumentException

`implementationType` must not be abstract, but `%s` is.

Error message

`implementationType` must not be abstract, but `%s` is.

What it means

TypeMapping's private constructor rejects an implementationType that is an abstract class (Modifier.isAbstract). A type mapping needs a concrete class it can actually instantiate when converting Pkl values to Java objects, so passing an abstract class or interface as the implementation is invalid.

Source

Thrown at pkl-config-java/src/main/java/org/pkl/config/java/mapper/TypeMapping.java:36

import java.lang.reflect.Modifier;
import org.jspecify.annotations.Nullable;

/**
 * Maps a type requested during conversion to the implementation type to be instantiated. The
 * requested type is often an interface type. The implementation type is always a class type. A
 * typical example is mapping {@link java.util.List} to {@link java.util.ArrayList}.
 */
// kept simple for now
// if we wanted to have more sophisticated mappings, we could:
// * support mapping factories/strategies (cf. ConverterFactory/Converter)
// * support parameterized mappings (e.g. Set<MyEnum> -> EnumSet<MyEnum>)
public final class TypeMapping<S, T extends S> {
  public final Class<S> requestedType;
  public final Class<T> implementationType;

  private TypeMapping(Class<S> requestedType, Class<T> implementationType) {
    if (Modifier.isAbstract(implementationType.getModifiers())) {
      throw new IllegalArgumentException(
          String.format(
              "`implementationType` must not be abstract, but `%s` is.",
              implementationType.getTypeName()));
    }
    if (!requestedType.isAssignableFrom(implementationType)) {
      throw new IllegalArgumentException(
          String.format(
              "`implementationType` must be assignable to `requestedType`, but `%s` is not assignable to `%s`.",
              implementationType.getTypeName(), requestedType.getTypeName()));
    }
    if (requestedType.isArray() || implementationType.isArray()) {
      throw new IllegalArgumentException("Type mappings are not supported for array types.");
    }
    this.requestedType = requestedType;
    this.implementationType = implementationType;
  }

  public static <S, T extends S> TypeMapping<S, T> of(

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Pass a concrete, instantiable class as the implementationType.
  2. If you only have the abstract type, select a concrete subclass to map to.
  3. Check with Modifier.isAbstract(impl.getModifiers()) before constructing the mapping.

Example fix

// before
var mapping = TypeMapping.of(Animal.class, Animal.class);
// after
var mapping = TypeMapping.of(Animal.class, Dog.class);
Defensive patterns

Strategy: validation

Validate before calling

if (Modifier.isAbstract(impl.getModifiers())) throw new IllegalArgumentException(impl + " must be concrete");

Type guard

static boolean isConcrete(Class<?> c) { return !c.isInterface() && !Modifier.isAbstract(c.getModifiers()); }

Try / catch

try { var m = TypeMapping.of(req, impl); } catch (IllegalArgumentException e) { /* surface which type was abstract */ }

Prevention

When it happens

Trigger: Calling TypeMapping.of(SomeAbstract.class, AbstractImpl.class) or a factory that forwards to the private constructor with a Class whose getModifiers() include the abstract bit (abstract classes, interfaces, annotation types).

Common situations: Mapping a Pkl module to a Java interface or abstract base class instead of a concrete subclass; picking the wrong class constant (e.g. List.class instead of ArrayList.class).

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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