apple/pkl · error · VmException

expectedPositiveNumber

expectedPositiveNumber

Error message

expectedPositiveNumber

What it means

`VmUtils.checkPositive` throws expectedPositiveNumber when given a negative long. Pkl APIs that require a count/size/length argument (e.g. repeat counts, string/list slicing sizes) funnel through this check.

Solutions

  1. Validate the argument is >= 0 at the call site before invoking the API.
  2. Fix the expression producing the negative number (order of operands, wrong sign).
  3. Clamp with `n.max(0)` if a zero-size result is acceptable.
  4. Add a config-level constraint (e.g. a property whose type excludes negatives) so bad values fail at validation.

Example fix

// before
val padding = (end - start).repeat("")
// after
val width = end - start
val padding = if (width > 0) "".repeat(width) else ""
Defensive patterns

Strategy: validation

Validate before calling

function requireNonNegative(n) { if (n < 0) throw new Error("expected non-negative value, got " + n); return n; }

Type guard

function isNonNegativeInt(n) { return Number.isInteger(n) && n >= 0; }

Try / catch

try { repeat(n) } catch (e) { /* negative count */ n = 0; }

Prevention

When it happens

Trigger: Passing a negative value to any stdlib operation that calls `VmUtils.checkPositive(n)`, such as `List.repeat(n)`, `String.repeat(n)`, or length/size arguments computed to be negative.

Common situations: Computing a repeat/slice count from a subtraction that underflows (e.g. `b - a` with b < a), user-supplied config numbers that were not validated.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at pkl-core/src/main/java/org/pkl/core/runtime/VmUtils.java:604

  @TruffleBoundary
  public static StringBuilder createBuilder() {
    return new StringBuilder();
  }

  @TruffleBoundary
  public static void appendToBuilder(StringBuilder builder, String string) {
    builder.append(string);
  }

  @TruffleBoundary
  public static String builderToString(StringBuilder builder) {
    return builder.toString();
  }

  public static void checkPositive(long n) {
    if (n < 0) {
      CompilerDirectives.transferToInterpreter();
      throw new VmExceptionBuilder().evalError("expectedPositiveNumber", n).build();
    }
  }

  public static Source loadSource(ResolvedModuleKey resolvedKey) {
    try {
      var text = resolvedKey.loadSource();
      return createSource(resolvedKey.getOriginal(), text);
    } catch (IOException e) {
      throw new VmExceptionBuilder()
          .evalError("ioErrorLoadingModule", resolvedKey.getOriginal().getUri())
          .withCause(e)
          .build();
    }
  }

  public static Source createSource(ModuleKey moduleKey, String text) {
    return Source.newBuilder("pkl", text, moduleKey.getUri().toString())
        .mimeType(VmLanguage.MIME_TYPE)

View on GitHub (pinned to f3efcbfc9b)