apple/pkl · error · IllegalArgumentException

`implementationType` must be assignable to `requestedType`,

Error message

`implementationType` must be assignable to `requestedType`, but `%s` is not assignable to `%s`.

What it means

The TypeMapping constructor verifies with requestedType.isAssignableFrom(implementationType) that the implementation class actually implements/extends the requested type. A mapping between unrelated types would produce ClassCastException later, so it is rejected up front.

Source

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

 * 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(
      Class<S> requestedType, Class<T> implementationType) {
    return new TypeMapping<>(requestedType, implementationType);
  }

  @Override
  public boolean equals(@Nullable Object obj) {

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Ensure the implementation type genuinely extends/implements the requested type.
  2. Swap the arguments if you inverted requestedType and implementationType.
  3. Add a compile-time bound (TypeMapping.of(S.class, T.class) with T extends S) so the compiler catches it.

Example fix

// before
TypeMapping.of(List.class, String.class);
// after
TypeMapping.of(List.class, ArrayList.class);
Defensive patterns

Strategy: validation

Validate before calling

if (!requested.isAssignableFrom(impl)) throw new IllegalArgumentException(impl + " is not a " + requested);

Type guard

static <S, T extends S> boolean assignable(Class<S> s, Class<T> t) { return s.isAssignableFrom(t); }

Try / catch

try { TypeMapping.of(req, impl); } catch (IllegalArgumentException e) { /* fix hierarchy or swap args */ }

Prevention

When it happens

Trigger: TypeMapping.of(S.class, T.class) where S is not a supertype of T, e.g. TypeMapping.of(String.class, Integer.class).

Common situations: Typos in generics erased by raw Class arguments; swapping the two arguments; refactoring a class hierarchy so the old implementation no longer extends the requested type.

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 apple/pkl@f3efcbfc9b (2026-09-08). Data as JSON: /api/errors/c12a36a2190cb076. Report an issue: GitHub.