apple/pkl · error · VmException

cannotRenderSubtypeForProtobuf

cannotRenderSubtypeForProtobuf

Error message

cannotRenderSubtypeForProtobuf

What it means

When rendering Pkl values to Protobuf, the renderer walks a class hierarchy and refuses if it encounters a value whose concrete class is a non-final subclass of the declared class being rendered. Protobuf rendering requires the exact class, so a value of a subtype that differs from the target class cannot be represented. The error names both the offending subtype and the expected class.

Solutions

  1. Change the property's declared type to the exact concrete class of the value so subtype and rendered class match
  2. Use Protobuf `oneof`/separate properties per subtype instead of a polymorphic base-typed property
  3. Choose a different output format (e.g. JSON/YAML/PBF) that supports subtyping via a converter

Example fix

// before
worker: Worker = new SshWorker { host = "h" }  // SshWorker != Worker for Protobuf
// after
worker: SshWorker = new SshWorker { host = "h" }
Defensive patterns

Strategy: type-guard

Validate before calling

// before rendering to protobuf, check the value's class equals the declared property type
function isExactClass(value: Any, expected: Class): Boolean = value.getClass() == expected
if (!isExactModulePolymorphicValues(module)) throw "polymorphic values cannot be protobuf-rendered"

Type guard

function rendersAsExactClass(value: Any, expected: Class): Boolean = value.getClass() == expected

Prevention

When it happens

Trigger: Rendering (Protobuf output) a property whose declared type is a non-final class but whose runtime value is an instance of a different subclass of that class, e.g. a property typed as a base class holding a derived-class instance.

Common situations: Config modules using class hierarchies (e.g. a `Worker` base class with `SshWorker`/`K8sWorker` subtypes) rendered to Protobuf; polymorphic collections where elements have mixed concrete types.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at pkl-core/src/main/java/org/pkl/core/stdlib/protobuf/RendererNodes.java:406

            writePropertyName();
            expectedName = Identifier.get("it_" + computedName);
            propertyPath.push(expectedName);
            startMessage();
          } else if (type instanceof ListingOrMappingTypeNode listingOrMappingType) {
            hasCollection = true;
            collectionType = listingOrMappingType.getValueTypeNode();
          } else if (type instanceof ListTypeNode listType) {
            hasCollection = true;
            collectionType = listType.getElementTypeNode();
          } else if (type instanceof MapTypeNode mapType) {
            hasCollection = true;
            collectionType = mapType.getValueTypeNode();
          } else if (type instanceof SetTypeNode setType) {
            hasCollection = true;
            collectionType = setType.getElementTypeNode();
          } else if (type instanceof NonFinalClassTypeNode) {
            if (type.getVmClass() != clazz) {
              throw new VmExceptionBuilder()
                  .evalError("cannotRenderSubtypeForProtobuf", type.getVmClass(), clazz)
                  .build();
            }
          }
        }
      }
      visit(value);
      if (hasWrapper) {
        endMessage();
        var popped = propertyPath.pop();
        assert popped == expectedName : "Corrupted traversal stack.";
      } else if (hasCollection) {
        collectionType = prevCollectionType;
      }
      var popped = propertyPath.pop();
      assert name == popped : "Corrupted traversal stack.";
    }

View on GitHub (pinned to f3efcbfc9b)