apple/pkl · error · VmException

expectedNonEmptyCollection

expectedNonEmptyCollection

Error message

expectedNonEmptyCollection

What it means

Pkl's non-empty collection types (`NonEmptyList`, `NonEmptySet`, `NonEmptyMap`, `NonEmptyString`) enforce their invariant lazily: `VmCollection.checkNonEmpty` throws `expectedNonEmptyCollection` as soon as an empty collection is constructed or accessed. The error carries the offending collection as program value `Collection`, so an empty value reached a non-empty-typed slot.

Source

Thrown at pkl-core/src/main/java/org/pkl/core/runtime/VmCollection.java:52

  public abstract boolean isEmpty();

  @Override
  public boolean isSequence() {
    return true;
  }

  public abstract VmCollection add(Object element);

  public abstract VmCollection concatenate(VmCollection other);

  public abstract Iterator<Object> reverseIterator();

  public abstract Builder<? extends VmCollection> builder();

  public final void checkNonEmpty() {
    if (isEmpty()) {
      CompilerDirectives.transferToInterpreter();
      throw new VmExceptionBuilder()
          .evalError("expectedNonEmptyCollection")
          .withProgramValue("Collection", this)
          .build();
    }
  }

  @SuppressWarnings("BooleanMethodIsAlwaysInverted")
  public abstract boolean isLengthOne();

  public final void checkLengthOne() {
    if (!isLengthOne()) {
      CompilerDirectives.transferToInterpreter();
      throw new VmExceptionBuilder()
          .evalError("expectedSingleElementCollection")
          .withProgramValue("Collection", this)
          .build();
    }
  }

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Supply at least one element to the collection at the reported property.
  2. Relax the schema type from the non-empty variant (`Listing!`/`Mapping!`) to `Listing`/`Mapping` if emptiness is legitimate.
  3. Add a conditional/default so an empty result falls back to a populated value instead of the non-empty type.
  4. Inspect the `Collection` value attached to the error to find exactly which property was empty.

Example fix

// before
hosts: NonEmptyList<String> = List.empty // error
// after
hosts: NonEmptyList<String> = List("localhost")
Defensive patterns

Strategy: validation

Validate before calling

// validate before assigning to a non-empty-typed property
if (hosts.isEmpty()) {
  throw new IllegalArgumentException("hosts must contain at least one entry");
}

Type guard

boolean isNonEmpty(List<?> list) { return list != null && !list.isEmpty(); }

Try / catch

catch (EvalError e) {
  if (e.getMessage().contains("expectedNonEmptyCollection")) {
    // inspect the 'Collection' program value; provide a default non-empty value
  }
}

Prevention

When it happens

Trigger: Amending/constructing a `Listing!`, `Mapping!`, `Set!`, or `String!` (non-empty variants) with zero elements; passing an empty List/Set/Map/Expr where a non-empty type is required; a `Listing!` whose contents are filtered down to nothing.

Common situations: Schema declares `serverHosts: NonEmptyList<String>` but the config renders an empty list; an environment with no matching entries produces an empty listing; default values that are empty lists assigned to non-empty typed properties.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08). Data as JSON: /api/errors/a683227421c1360a. Report an issue: GitHub.