apple/pkl · error · VmException

cannotRenderProtobufMapKeyType

cannotRenderProtobufMapKeyType

Error message

cannotRenderProtobufMapKeyType

What it means

Protobuf map keys can only be integral, string, or boolean types. When the renderer resolves a `Mapping` type whose key type resolves to anything else (e.g. Int8/UInt16, DataSize, enum, or class types), it throws this error.

Source

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

        return resolveType(nullableType.getElementTypeNode());
      } else if (type instanceof TypeAliasTypeNode typeAliasType) {
        return resolveType(typeAliasType.getVmTypeAlias().getTypeNode());
      } else if (type instanceof ListingTypeNode listingType) {
        var valueType = resolveType(listingType.getValueTypeNode());
        assert valueType != null : "Failed to resolve type node.";

        type =
            requiresWrapper()
                ? null
                : new ListingTypeNode(
                    VmUtils.unavailableSourceSection(), VmLanguage.get(null), valueType);
        return type;
      } else if (type instanceof MappingTypeNode mappingType) {
        var keyType = resolveType(mappingType.getKeyTypeNode());
        if (!(keyType instanceof IntTypeNode
            || keyType instanceof StringTypeNode
            || keyType instanceof BooleanTypeNode)) {
          throw new VmExceptionBuilder()
              .evalError("cannotRenderProtobufMapKeyType")
              .withSourceSection(type.getSourceSection())
              .build();
        }
        var valueType = resolveType(mappingType.getValueTypeNode());
        assert valueType != null : "Incomplete or malformed Mapping type";
        mappingType =
            new MappingTypeNode(
                VmUtils.unavailableSourceSection(), VmLanguage.get(null), keyType, valueType);

        type = requiresWrapper() ? null : mappingType;
        return type;
      } else if (type instanceof UnionTypeNode) {
        // Some non-obvious normalization going on here:
        // - A union type resolves to a union type, unless all element types are string or string
        // literal types.
        // - All element types are resolved also.
        // - All string literal types are combined into a single String case.

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Change the map key type to `String` (most compatible) and encode structured keys as strings
  2. Use `Int` or `Boolean` keys if semantically appropriate
  3. Render to JSON/YAML instead of Protobuf, which allows arbitrary keys
  4. Pre-process the mapping into a Listing of entries with `key`/`value` properties

Example fix

// before
ports: Mapping<Int16, String>
// after
ports: Mapping<String, String>  // keys as strings, e.g. "8080" -> "http"
Defensive patterns

Strategy: validation

Validate before calling

function hasProtobufSafeKeys(m: Mapping): Boolean =
  m.keys.every((k) -> k is String || k is Int || k is Boolean)

Type guard

function protobufSafeMap(m: Mapping): Boolean = m.keys.every((k) -> k is String || k is Int || k is Boolean)

Prevention

When it happens

Trigger: Rendering a `Mapping<K, V>` to Protobuf where K resolves to a type other than Int, String, or Boolean, e.g. `Mapping<Int128, String>` or a Listing/enum-keyed mapping.

Common situations: Maps keyed by enums, custom key classes, or wide integer types (Int8, UInt, Float) exported to protobuf.

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/95e3f694b233abb8. Report an issue: GitHub.