apple/pkl · error · VmException

invalidProtobufTopLevelValue

invalidProtobufTopLevelValue

Error message

invalidProtobufTopLevelValue

What it means

The Protobuf renderer rejects top-level values that are internal Pkl value kinds (VmValue subclasses) other than Typed objects and Duration. Values like IntSeq, Pair, Regex, etc. have no protobuf mapping at the document root, and the error reports the stripped Vm-prefixed class name.

Source

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

    public ProtobufRenderer(StringBuilder builder, String indent, PklConverter converter) {
      super("Protobuf", builder, indent, converter, true, true);
    }

    @Override
    protected void visitDocument(Object value) {
      visit(value);
      startNewLine();
    }

    @Override
    protected void visitTopLevelValue(Object value) {
      if (value instanceof VmValue && !(value instanceof VmTyped || value instanceof VmDuration)) {
        var name = value.getClass().getSimpleName();
        if (name.startsWith("Vm")) {
          name = name.substring(2);
        }
        throw new VmExceptionBuilder().evalError("invalidProtobufTopLevelValue", name).build();
      }
      assert propertyPath.isEmpty() && wrapperRequirement.isEmpty() : "Corrupted traversal stack.";
      wrapperRequirement.push(false);
      visit(value);
      var wrap = wrapperRequirement.pop();
      assert !wrap && propertyPath.isEmpty() && wrapperRequirement.isEmpty()
          : "Corrupted traversal stack.";
    }

    @Override
    protected void visitRenderDirective(VmTyped value) {
      writePropertyName();
      // append verbatim
      builder.append(VmUtils.readTextProperty(value));
    }

    @Override
    protected void startDynamic(VmDynamic value) {

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Render a typed object (module/class instance) as the top-level value
  2. Convert primitives/Duration-like values appropriately (Duration is allowed; e.g. wrap others in a typed object)
  3. Switch to JSON/Pcf/YAML renderer for non-object top-level values

Example fix

// before
output.value = Pair(1, 2)
output.renderer = new ProtobufRenderer
// after
output.value = new { pair = new Dynamic { first = 1; second = 2 } }
output.renderer = new ProtobufRenderer
Defensive patterns

Strategy: validation

Validate before calling

function isProtobufTopLevel(v) { return isPklTyped(v) || isDuration(v); }

Type guard

const isProtobufTopLevelOk = (v) => isPklTyped(v) || isPklDuration(v);

Try / catch

try { renderAsProtobuf(value) } catch (e) { if (e.code === 'invalidProtobufTopLevelValue') { /* wrap in typed object */ } else throw e }

Prevention

When it happens

Trigger: Calling protobuf rendering with a top-level value such as an IntSeq, Pair, Listing-internal value, Regex, or other non-Typed/non-Duration VmValue.

Common situations: Pointing a protobuf output at `output.value` that holds a Pkl collection or utility value instead of a typed object; migrating a renderer from JSON (permissive) to protobuf (restrictive).

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