apple/pkl · error · VmException

typeMismatch

typeMismatch

Error message

Expected value of type `{0}`, but got type `{1}`.

What it means

The predicate of a `when` member must evaluate to a Boolean. When the predicate node's boolean execution fails with an UnexpectedResultException, Pkl reports the actual value's type against the expected `Boolean` type.

Solutions

  1. Make the predicate explicitly Boolean: use comparisons (`x == y`, `x != null`) rather than raw values.
  2. Convert or validate the value before the predicate (`x.isEmpty()`, `x > 0`, `x != null`).
  3. Check the actual value type reported in the error and fix the underlying property's type or default.

Example fix

// before
when (config.enabled) { ... } // enabled is a String
// after
when (config.enabled == "yes") { ... }
Defensive patterns

Strategy: validation

Validate before calling

// Ensure predicates are Boolean before use
when (config.enabled is Boolean) {
  when (config.enabled) { ... }
}

Type guard

function isBoolean(v: Any): Boolean = v is Boolean

Prevention

When it happens

Trigger: `when (expr) { ... }` where `expr` evaluates to a non-Boolean (e.g. a String, Int, or null) inside a Dynamic, Mapping, or Listing generator; raised in addMembers during evalDynamic/evalMapping/evalListing.

Common situations: Using a truthy non-Boolean condition (e.g. `when (config.enabled)` where enabled is a String "yes"); forgetting `!= null` and passing null into the predicate; returning an Int count where a comparison was intended.

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/0634d5a38da22b89. Report an issue: GitHub.

Appendix: source

Thrown at pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorPredicateMemberNode.java:116

        if (value == null) {
          var constantValue = member.getConstantValue();
          if (constantValue != null) {
            value = constantValue;
          } else {
            var callTarget = member.getCallTarget();
            value = callTarget.call(parent, owner, key);
          }
          owner.setCachedValue(key, value);
        }

        frame.setAuxiliarySlot(customThisSlot, value);

        try {
          var isApplicable = predicateNode.executeBoolean(frame);
          if (isApplicable) data.addMember(frame, key, this.member, this);
        } catch (UnexpectedResultException e) {
          CompilerDirectives.transferToInterpreter();
          throw exceptionBuilder()
              .typeMismatch(e.getResult(), BaseModule.getBooleanClass())
              .withLocation(predicateNode)
              .build();
        }
      }
    }

    // restore previous value
    // handles the (pathetic) case of a predicate containing an object with another predicate
    frame.setAuxiliarySlot(customThisSlot, previousValue);
  }

  private void initThisSlot(VirtualFrame frame) {
    if (customThisSlot == -1) {
      CompilerDirectives.transferToInterpreterAndInvalidate();
      // deferred until execution time s.t. nodes of inlined type aliases get the right frame slot
      customThisSlot =
          frame.getFrameDescriptor().findOrAddAuxiliarySlot(VmUtils.CUSTOM_THIS_FRAME_SLOT_ID);

View on GitHub (pinned to f3efcbfc9b)