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
- Define `options` as a plain (non-abstract) class and type the property with it
- Remove unions, constraints, type aliases, or intersections from the options type
- 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
- Always define a dedicated concrete options class per command
- Avoid unions/aliases/constraints on the options property
- Confirm the referenced identifier is a class, not a module property
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
- commandFlagInvalidType
- +e.getMessage()
- commandSubcommandConflict
- commandOptionsTypeAbstractClass
- commandOptionBothFlagAndArgument
AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08).
Data as JSON: /api/errors/12f62ea26868a4fb.
Report an issue: GitHub.