{"record":{"id":"118829dfc582f602","repo":"apple/pkl","slug":"objectcannothavepredicatemember","errorCode":"objectCannotHavePredicateMember","errorMessage":"Object of type `{0}` cannot have a predicate member.","messagePattern":"Object of type `(.+?)` cannot have a predicate member\\.","errorType":"exception","errorClass":"VmException","httpStatus":null,"severity":"error","filePath":"pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorPredicateMemberNode.java","lineNumber":72,"sourceCode":"\n  @Specialization\n  @SuppressWarnings(\"unused\")\n  protected void evalListing(VirtualFrame frame, VmListing parent, ObjectData data) {\n    addMembers(frame, parent, data);\n  }\n\n  @Fallback\n  @SuppressWarnings(\"unused\")\n  void fallback(Object parent, ObjectData data) {\n    if (parent == BaseModule.getDynamicClass()\n        || parent == BaseModule.getMappingClass()\n        || parent == BaseModule.getListingClass()) {\n      // nothing to do (parent is guaranteed to have zero elements/entries)\n      return;\n    }\n\n    CompilerDirectives.transferToInterpreter();\n    throw exceptionBuilder()\n        .evalError(\n            \"objectCannotHavePredicateMember\",\n            parent instanceof VmClass ? parent : VmUtils.getClass(parent))\n        .withLocation(predicateNode)\n        .build();\n  }\n\n  private void addMembers(VirtualFrame frame, VmObject parent, ObjectData data) {\n    initThisSlot(frame);\n\n    var previousValue = frame.getAuxiliarySlot(customThisSlot);\n    var visitedKeys = EconomicSets.create();\n\n    // do our own traversal instead of relying on `VmAbstractObject.force/iterateMemberValues`\n    // (more efficient and we don't want to execute `predicateNode` behind Truffle boundary)\n    for (var owner = parent; owner != null; owner = owner.getParent()) {\n      var entries = EconomicMaps.getEntries(owner.getMembers());\n      while (entries.advance()) {","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorPredicateMemberNode.java#L54-L90","documentation":"Predicate members (`when (cond) { ... }` / `whenDefined`) are only allowed on Dynamic, Listing, and Mapping objects. Applying a predicate member to any other object type (a typed class instance, Number, etc.) is rejected with the offending type named.","triggerScenarios":"Declaring `when (...) { ... }` inside an amendment of an object whose type is not Dynamic/Listing/Mapping — e.g. amending an instance of a typed Pkl class or a base value. Raised in the public fallback of GeneratorPredicateMemberNode when the parent isn't the allowed parent classes.","commonSituations":"Adding `when` blocks inside typed class amendments to conditionally set properties; adding predicate members to List/List-like values; using `when` in a module-level `amends` of a typed schema class.","solutions":["Move the conditional logic outside the object: compute values with `if` expressions on properties instead of `when` members.","For typed classes, conditionally include properties via `default`-value expressions or by building a Dynamic in the amender and converting.","Confirm the amended type is Dynamic, Listing, or Mapping before using predicate members."],"exampleFix":"// before\nserver {\n  when (env == \"dev\") { replicas = 1 }\n}\n// after\nserver {\n  replicas = if (env == \"dev\") 1 else 3\n}","handlingStrategy":"type-guard","validationCode":"// Only use `when` members if the parent is Dynamic/Listing/Mapping\nwhen (parent is Dynamic || parent is Listing || parent is Mapping) {\n  // predicate member allowed here\n}","typeGuard":"function supportsPredicateMembers(v: Any): Boolean =\n  v is Dynamic || v is Listing || v is Mapping","tryCatchPattern":null,"preventionTips":["Use `if` expressions on property values for typed classes instead of `when` members.","Restrict `when` usage to Dynamic/Listing/Mapping bodies.","Keep conditional logic in computed properties for typed schemas."],"tags":["pkl","predicate-member","when","typed-class"],"backgroundTag":"unsupported-operation","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}