apple/pkl · error · VmTypeMismatchException.ClassType

VmTypeMismatchException.ClassType (value is not a subclass…

Error message

VmTypeMismatchException.ClassType (value is not a subclass of the expected class)

What it means

ClassClassTypeNode validates that a `Class` value (a Pkl class object) is a subclass of the class expected by the `Class<T>` type argument. When value.isSubclassOf(clazz) fails, it throws VmTypeMismatchException.ClassType reporting the expected class.

Solutions

  1. Pass a class object that is T or a subclass of T.
  2. Widen the type argument to a common superclass (or `Any`) if unrelated classes must be accepted.
  3. Check the expected class named in the error and verify the hierarchy with `isSubclassOf`.

Example fix

// before
elemClass: Class<Animal> = Robot
// after (Robot is not an Animal)
elemClass: Class<Any> = Robot
Defensive patterns

Strategy: validation

Validate before calling

// Pkl: check subclass relation before passing a class object
function checkClass(c: Class): Boolean = c.isSubclassOf(Animal)

Type guard

value is Class && value.isSubclassOf(Animal)

Prevention

When it happens

Trigger: A `Class<T>` annotation (e.g. `Class<Animal>`) receives a class object that is not T or a subclass of T (e.g. the class object `Unrelated`), or a sibling class at the same level.

Common situations: Factory/registry patterns that pass class objects around (`elementClass: Class<...>`); changing a class hierarchy so a previously-passing class object no longer derives from T; typos selecting the wrong class.

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/7730d6b7ab79ce2d. Report an issue: GitHub.

Appendix: source

Thrown at pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java:3282

      // safe to init clazz here (instead of on init and typealias instantiate)
      // because in the typealias case this node will never execute prior to instantiation
      initVmClass();

      // Fast path: all classes match Class<Any> / Class<unknown> / Class<type arg>.
      // In this case, skip the subclass check and behave like a bare `Class` type annotation.
      if (clazz == BaseModule.getAnyClass()) {
        return value;
      }

      // clazz will be null iff the type arg is a not a valid class type
      if (clazz == null) {
        CompilerDirectives.transferToInterpreter();
        throw new VmTypeMismatchException.ClassType(sourceSection, value, typeNode.doExport());
      }

      if (!value.isSubclassOf(clazz)) {
        CompilerDirectives.transferToInterpreter();
        throw new VmTypeMismatchException.ClassType(sourceSection, value, clazz);
      }

      return value;
    }

    @Fallback
    protected Object fallback(Object value) {
      throw typeMismatch(value, BaseModule.getClassClass());
    }

    @Override
    protected boolean acceptTypeNode(boolean visitTypeArguments, TypeNodeConsumer consumer) {
      if (visitTypeArguments) {
        return consumer.accept(this) && typeNode.acceptTypeNode(visitTypeArguments, consumer);
      }
      return consumer.accept(this);
    }

View on GitHub (pinned to f3efcbfc9b)