apple/pkl · error · VmException

cannotIterateOverThisValue

cannotIterateOverThisValue

Error message

Cannot iterate over value of type `{0}`.

What it means

Thrown when a generator's `for` expression tries to iterate a value that is not iterable (not a List, Set, Map, String, Listing, Mapping, etc.). The `@Fallback` node catches any value the specialized iterable nodes cannot handle and reports the value's type.

Solutions

  1. Check the type of the iterated expression; wrap scalars in a list (`[expr]`) if iteration over a single value is intended.
  2. Use the nullish/type check pattern: only iterate when the value is a collection (e.g. `person.names.toList()`).
  3. Inspect the 'Value' attached to the error message to see the actual runtime value and correct the expression.

Example fix

// before
for (tag in contact.tags) { ... } // tags may be a single String
// after
for (tag in contact.tags.toList()) { ... }
Defensive patterns

Strategy: type-guard

Validate before calling

// Pkl: guard before iterating
when (value is List || value is Set || value is Listing) {
  for (x in value) { ... }
}

Type guard

function isIterable(v: Any): Boolean =
  v is List || v is Set || v is Map || v is Listing || v is Mapping || v is String

Prevention

When it happens

Trigger: `for (x in expr)` where `expr` evaluates to a non-iterable value — e.g. an Int, Boolean, a class instance, or a module object instead of a collection. Raised in the `fallback` node of GeneratorForNode.

Common situations: Iterating over a property that is a scalar (`for (p in person.name)`); forgetting that a nullable or default value resolved to `null`/Int instead of a List; iterating a module or class reference instead of one of its collections.

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

Appendix: source

Thrown at pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorForNode.java:144

    var length = iterable.getLength();
    for (long key = 0, value = iterable.start; key < length; key++, value += iterable.step) {
      executeIteration(frame, parent, data, key, value);
    }
  }

  @Specialization
  protected void eval(VirtualFrame frame, Object parent, ObjectData data, VmBytes iterable) {
    long idx = 0;
    for (var byt : iterable.getBytes()) {
      executeIteration(frame, parent, data, idx++, (long) byt);
    }
  }

  @Fallback
  @SuppressWarnings("unused")
  protected void fallback(VirtualFrame frame, Object parent, ObjectData data, Object iterable) {
    CompilerDirectives.transferToInterpreter();
    throw exceptionBuilder()
        .evalError("cannotIterateOverThisValue", VmUtils.getClass(iterable))
        .withLocation(iterableNode)
        .withProgramValue("Value", iterable)
        .build();
  }

  private void doEvalObject(VirtualFrame frame, VmObject iterable, Object parent, ObjectData data) {
    iterable.forceAndIterateMemberValues(
        (key, member, value) -> {
          var convertedKey = member.isProp() ? key.toString() : key;
          // TODO: Executing iteration behind a Truffle boundary is bad for performance.
          // This and similar cases will be fixed in an upcoming PR that replaces method
          // `(forceAnd)iterateMemberValues` with cursor-based external iterators.
          executeIteration(frame, parent, data, convertedKey, value);
          return true;
        });
  }

View on GitHub (pinned to f3efcbfc9b)