apple/pkl · error · VmException

classMustBeLocal

classMustBeLocal

Error message

classMustBeLocal

What it means

When amending a module or object (`extends`/amendment position), Pkl requires that class definitions be `local` — a non-local class in an amendment would leak a new top-level type into the amended parent, which the language forbids. AstBuilder.visitClasses raises classMustBeLocal with the class header section when moduleInfo.isAmend() and the visited class member is not local.

Source

Thrown at pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java:1779

      List<Class> classes,
      List<TypeAlias> typeAliases,
      List<ClassProperty> properties,
      Set<String> propertyNames,
      ModuleInfo moduleInfo) {

    var totalSize = imports.length + classes.size() + typeAliases.size() + properties.size();
    var result = EconomicMaps.<Object, ObjectMember>create(totalSize);

    for (var member : imports) {
      checkDuplicateMember(member.getName(), member.getHeaderSection(), propertyNames);
      EconomicMaps.put(result, member.getName(), member);
    }

    for (var clazz : classes) {
      ObjectMember member = visitClass(clazz);

      if (moduleInfo.isAmend() && !member.isLocal()) {
        throw exceptionBuilder()
            .evalError("classMustBeLocal")
            .withSourceSection(member.getHeaderSection())
            .build();
      }

      checkDuplicateMember(member.getName(), member.getHeaderSection(), propertyNames);
      EconomicMaps.put(result, member.getName(), member);
    }

    for (var typeAlias : typeAliases) {
      var member = visitTypeAlias(typeAlias);

      if (moduleInfo.isAmend() && !member.isLocal()) {
        throw exceptionBuilder()
            .evalError("typeAliasMustBeLocal")
            .withSourceSection(member.getHeaderSection())
            .build();
      }

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Add the `local` modifier: `local class Foo {}`.
  2. Move the class into the base module instead of the amendment.
  3. Convert the class to a type alias or function local if a full class is unnecessary.

Example fix

// before (in an amendment)
class Credentials { user: String }

// after
local class Credentials { user: String }
Defensive patterns

Strategy: validation

Validate before calling

// Verify classes declared in amendment files are local:
if (isAmendmentFile(file) && line.matches("\\s*class\\s+\\w+.*")) {
  report(file, lineNo, "classes in amendments must be `local`");
}

Prevention

When it happens

Trigger: Writing a class inside a module amendment or object amendment (isAmend() == true) without the `local` modifier, e.g. `class Foo {}` at amendment top level.

Common situations: Creating an override/layer file that extends a base config and adding helper classes; migrating code from a base module into an amendment without realizing nested classes need `local`.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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