{"record":{"id":"a3b27b9c4b12ae83","repo":"apple/pkl","slug":"classmustbelocal","errorCode":"classMustBeLocal","errorMessage":"classMustBeLocal","messagePattern":"classMustBeLocal","errorType":"error_code","errorClass":"VmException","httpStatus":null,"severity":"error","filePath":"pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java","lineNumber":1779,"sourceCode":"      List<Class> classes,\n      List<TypeAlias> typeAliases,\n      List<ClassProperty> properties,\n      Set<String> propertyNames,\n      ModuleInfo moduleInfo) {\n\n    var totalSize = imports.length + classes.size() + typeAliases.size() + properties.size();\n    var result = EconomicMaps.<Object, ObjectMember>create(totalSize);\n\n    for (var member : imports) {\n      checkDuplicateMember(member.getName(), member.getHeaderSection(), propertyNames);\n      EconomicMaps.put(result, member.getName(), member);\n    }\n\n    for (var clazz : classes) {\n      ObjectMember member = visitClass(clazz);\n\n      if (moduleInfo.isAmend() && !member.isLocal()) {\n        throw exceptionBuilder()\n            .evalError(\"classMustBeLocal\")\n            .withSourceSection(member.getHeaderSection())\n            .build();\n      }\n\n      checkDuplicateMember(member.getName(), member.getHeaderSection(), propertyNames);\n      EconomicMaps.put(result, member.getName(), member);\n    }\n\n    for (var typeAlias : typeAliases) {\n      var member = visitTypeAlias(typeAlias);\n\n      if (moduleInfo.isAmend() && !member.isLocal()) {\n        throw exceptionBuilder()\n            .evalError(\"typeAliasMustBeLocal\")\n            .withSourceSection(member.getHeaderSection())\n            .build();\n      }","sourceCodeStart":1761,"sourceCodeEnd":1797,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java#L1761-L1797","documentation":"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.","triggerScenarios":"Writing a class inside a module amendment or object amendment (isAmend() == true) without the `local` modifier, e.g. `class Foo {}` at amendment top level.","commonSituations":"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`.","solutions":["Add the `local` modifier: `local class Foo {}`.","Move the class into the base module instead of the amendment.","Convert the class to a type alias or function local if a full class is unnecessary."],"exampleFix":"// before (in an amendment)\nclass Credentials { user: String }\n\n// after\nlocal class Credentials { user: String }","handlingStrategy":"validation","validationCode":"// Verify classes declared in amendment files are local:\nif (isAmendmentFile(file) && line.matches(\"\\\\s*class\\\\s+\\\\w+.*\")) {\n  report(file, lineNo, \"classes in amendments must be `local`\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat amendment files as value-override layers only; keep types in the base module.","Grep overrides for bare `class ` declarations and prefix with `local`.","Remember the rule: anything declaring a type (class/typealias) in an amendment needs `local`."],"tags":["pkl","ast","amendment","class-definition"],"backgroundTag":"invalid-config-value","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}