apple/pkl · error · VmTypeMismatchException.Union

union type mismatch

Error message

union type mismatch

What it means

Pkl's UnionTypeNode checks a value against each member of a union type (e.g. `Int|String|Listing<Float>`). It tries every member, collecting each member's VmTypeMismatchException; if none succeeds, it throws VmTypeMismatchException.Union summarizing why every union member rejected the value. This is Pkl's standard 'value matches none of the union alternatives' type-check failure.

Source

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

      // if enabled, re-execute type checks to generate power assertions
      localContext.setInTypeTest(wasInTypeTest);
      if (VmContext.get(this).getPowerAssertionsEnabled()
          && (!wasInTypeTest || localContext.hasActiveTracker())) {
        for (var i = 0; i < elementTypeNodes.length; i++) {
          var elementTypeNode = elementTypeNodes[i];
          try {
            if (shouldEagerCheck) {
              elementTypeNode.executeEagerly(frame, value);
            } else {
              elementTypeNode.executeLazily(frame, value);
            }
          } catch (VmTypeMismatchException e) {
            typeMismatches[i] = e;
          }
        }
      }

      throw new VmTypeMismatchException.Union(sourceSection, value, this, typeMismatches);
    }

    @Override
    public Object executeEagerly(VirtualFrame frame, Object value) {
      // escape analysis should remove this allocation in compiled code
      var typeMismatches = new VmTypeMismatchException[elementTypeNodes.length];

      // disallow power assertions from triggering in case one union member checks successfully
      var localContext = VmLanguage.get(this).localContext.get();
      var wasInTypeTest = localContext.isInTypeTest();
      localContext.setInTypeTest(true);

      for (var i = 0; i < elementTypeNodes.length; i++) {
        // eager checks
        try {
          var result = elementTypeNodes[i].executeEagerly(frame, value);
          localContext.setInTypeTest(wasInTypeTest);
          return result;

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Fix the value so it matches one of the union's members (check the per-member messages in the error output).
  2. Widen the union type in the schema to include the value's actual type (e.g. add `Null` if the value can be absent).
  3. If the value comes from an external source, coerce or validate it before feeding it to the module.
  4. Check for typos in string-literal union members.

Example fix

// before
name: "dev" | "prod" | "staging"
// config sets name = "Prod"
// after
name: "dev" | "prod" | "staging"
// config sets name = "prod"
Defensive patterns

Strategy: validation

Validate before calling

// Pkl: guard before assigning to a union-typed property
function checkName(v: String): String =
  if (v == "dev" || v == "prod" || v == "staging") v
  else throw("name must be one of dev|prod|staging, got: \(v)")

Type guard

// Pkl type test
x is Int | String

Prevention

When it happens

Trigger: A property/type-alias/type-argument annotated with a union type receives a value that fails every member's type check, e.g. a property `x: Int|String` assigned `true`, or `Listing<Int>|Listing<String>` given elements of neither element type. Raised lazily (executeLazily) or eagerly depending on member types.

Common situations: Config values coming from external sources (CLI flags, environment substitution, rendered/amend values) whose inferred Pkl value is not one of the declared union alternatives; typos in string-literal unions; widening a property's union type without updating all importing modules.

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/1e2f892945d03d14. Report an issue: GitHub.