apple/pkl · error
commandOptionNoTypeAnnotation
commandOptionNoTypeAnnotation
Error message
commandOptionNoTypeAnnotation
What it means
Thrown when a CLI option, flag, or argument property has no type annotation at all (e.g. just `verbosity:`, relying on amending). The command framework derives value parsing and validation from the declared type, so a property collected as a CLI parameter must carry an explicit type annotation.
Solutions
- Add an explicit type annotation to the property (e.g. `verbose: Boolean`)
- If inheriting from a base class, redeclare the property with its type in the command spec class
- Use a supported type (String, Int, Boolean, List, Listing, Map, etc.)
Example fix
// before @Option verbose: // after @Option verbose: Boolean
Defensive patterns
Strategy: validation
Validate before calling
// ensure every CLI parameter property has a type annotation before spec parse
for (prop in commandClass.properties) {
if (prop.hasAnnotation("Option") && prop.type == null) throw "missing type annotation"
} Prevention
- Never use amendment-style untyped properties in command spec classes
- Always write an explicit supported type on @Option/@Flag/@CountedFlag/@Argument properties
When it happens
Trigger: Declaring a @Option/@Flag/@CountedFlag/@Argument property without a type, e.g. `@Option verbose:` — resolveType finds prop.getTypeNode() == null and throws.
Common situations: Amending-style shorthand Pkl habits carried into a command spec class; deleting a type during refactoring; relying on base-class property types without redeclaring.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- commandArgumentsMultipleRepeated
- commandArgumentUnexpectedNonRepeatedNullableType
- commandFlagInvalidType
- commandFlagNameCollision
- commandMustNotAssignOrAmendProperty
AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08).
Data as JSON: /api/errors/82ca4283df153819.
Report an issue: GitHub.
Appendix: source
Thrown at pkl-core/src/main/java/org/pkl/core/runtime/CommandSpecParser.java:393
.build();
}
return new CommandSpec.Argument(
prop.getName().toString(),
VmUtils.exportDocComment(prop.getDocComment()),
behavior.getEach(),
behavior.getAll(),
behavior.getCompletionCandidates(),
behavior.getMultiple());
}
/** Unwrap nullables, constraints, and aliases and return Pair(underlying type, is nullable) */
private Pair<TypeNode, Boolean> resolveType(ClassProperty prop) {
var propertyTypeNode = prop.getTypeNode();
if (propertyTypeNode != null) {
return resolveType(propertyTypeNode.getTypeNode());
}
throw exceptionBuilder()
.withSourceSection(prop.getHeaderSection())
.evalError("commandOptionNoTypeAnnotation", prop.getName())
.build();
}
/** Unwrap nullables, constraints, and aliases and return Pair(underlying type, is nullable) */
private Pair<TypeNode, Boolean> resolveType(TypeNode typeNode) {
var isNullable = false;
while (true) {
if (typeNode instanceof TypeNode.NullableTypeNode nullableTypeNode) {
isNullable = true;
typeNode = nullableTypeNode.getElementTypeNode();
} else if (typeNode instanceof TypeNode.ConstrainedTypeNode constrainedTypeNode) {
typeNode = constrainedTypeNode.getChildTypeNode();
} else if (typeNode instanceof TypeNode.TypeAliasTypeNode typeAliasTypeNode) {
if (typeAliasTypeNode.getVmTypeAlias() == BaseModule.getCharTypeAlias()) break;
typeNode = typeAliasTypeNode.getAliasedTypeNode();
} else {View on GitHub (pinned to f3efcbfc9b)