apple/pkl · error

commandOptionsTypeNotClass

commandOptionsTypeNotClass

Error message

commandOptionsTypeNotClass

What it means

getOptionsClass throws commandOptionsTypeNotClass when a command's `options` property is not typed as a plain user class. Only a concrete user class (or the Base `Typed` class) is supported for CLI options; unions, aliases, constrained types, strings, etc. are rejected, with the offending type's source text in the message.

Source

Thrown at pkl-core/src/main/java/org/pkl/core/runtime/CommandSpecParser.java:177

  private VmClass getOptionsClass(VmTyped command) {
    var optionsProperty = command.getVmClass().getProperty(Identifier.OPTIONS);
    if (optionsProperty == null) {
      // at this point we've asserted the command extends pkl:Command
      throw PklBugException.unreachableCode();
    }
    var optionsPropertyTypeNode = optionsProperty.getTypeNode();
    if (optionsPropertyTypeNode == null) {
      // at this point we've asserted the options property exists and that it is neither amended nor
      // assigned
      // the only possibility here is that it has a type annotation, otherwise this wouldn't parse
      throw PklBugException.unreachableCode();
    }
    var optionsTypeNode = optionsPropertyTypeNode.getTypeNode();
    if (optionsTypeNode instanceof TypeNode.TypedTypeNode) {
      return BaseModule.getTypedClass();
    }
    if (!(optionsTypeNode instanceof TypeNode.UserClassTypeNode node)) {
      throw exceptionBuilder()
          .withSourceSection(optionsTypeNode.getSourceSection())
          .evalError(
              "commandOptionsTypeNotClass", optionsTypeNode.getSourceSection().getCharacters())
          .build();
    }
    var clazz = node.getVmClass();
    if (clazz.isAbstract()) {
      throw exceptionBuilder()
          .withSourceSection(clazz.getHeaderSection())
          .evalError("commandOptionsTypeAbstractClass", clazz.getQualifiedName())
          .build();
    }
    return clazz;
  }

  private Iterable<CommandSpec.Option> collectOptions(VmClass optionsClass) {
    CommandSpec.Argument lastRepeatedArg = null;
    EconomicMap<String, CommandSpec.Option> opts = EconomicMap.create();

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Define `options` as a plain (non-abstract) class and type the property with it
  2. Remove unions, constraints, type aliases, or intersections from the options type
  3. Check that the referenced name resolves to a class, not a module property or function

Example fix

// before
options: String
// after
class Options { ... }
options: Options
Defensive patterns

Strategy: type-guard

Validate before calling

// pkl-side check before wiring CLI
class Options { ... }
options: Options // must be a plain class type

Type guard

function isPlainClassType(t) { return t && !t.union && !t.constraint && !t.alias && typeof t === "class"; }

Prevention

When it happens

Trigger: Declaring `options: String` or `options: A|B` or `options: A(n: 1)` — the options property's type node is not a TypeNode.UserClassTypeNode (nor TypedTypeNode).

Common situations: Typing options as an interface/union/alias instead of a class; using a constrained type like `options: Options/*.foo = 1*/`; accidentally pointing options at a non-class module member.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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