apple/pkl · error
commandOptionBothFlagAndArgument
commandOptionBothFlagAndArgument
Error message
commandOptionBothFlagAndArgument
What it means
collectOptions throws commandOptionBothFlagAndArgument when a single options-class property carries both a @Flag-style (boolean flag) annotation and an @Arg-style (positional argument) annotation. An option must be either a flag or a positional argument, never both; the property's header section identifies the conflict.
Source
Thrown at pkl-core/src/main/java/org/pkl/core/runtime/CommandSpecParser.java:217
for (var prop : clazz.getDeclaredProperties()) {
var name = prop.getName().toString();
if (VmModifier.isLocalOrExternalOrAbstractOrFixedOrConst(prop.getModifiers())
|| opts.containsKey(name)) continue;
VmTyped flagAnnotation = null;
VmTyped argAnnotation = null;
for (var annotation : prop.getAllAnnotations(true)) {
if (annotation.getVmClass().isSubclassOf(CommandModule.getBaseFlagClass())) {
if (flagAnnotation != null) continue;
flagAnnotation = annotation;
} else if (annotation.getVmClass() == CommandModule.getArgumentClass()) {
if (argAnnotation != null) continue;
argAnnotation = annotation;
}
}
if (flagAnnotation != null && argAnnotation != null) {
throw exceptionBuilder()
.withSourceSection(prop.getHeaderSection())
.evalError("commandOptionBothFlagAndArgument", prop.getName())
.build();
}
if (argAnnotation != null) {
var arg = collectArgument(prop, argAnnotation);
opts.put(arg.name(), arg);
if (arg.repeated()) {
if (lastRepeatedArg == null) {
lastRepeatedArg = arg;
} else {
throw exceptionBuilder()
.withSourceSection(optionsClass.getHeaderSection())
.evalError("commandArgumentsMultipleRepeated", lastRepeatedArg.name(), arg.name())
.build();
}
}View on GitHub (pinned to f3efcbfc9b)
Solutions
- Remove one of the two annotations from the property
- Keep the @Argument annotation and drop the flag one if it should be positional
- Keep the flag annotation and drop @Argument if it should be an option-style flag
Example fix
// before
@BooleanFlag { name = "verbose" }
@Argument
verbose: Boolean
// after
@BooleanFlag { name = "verbose" }
verbose: Boolean Defensive patterns
Strategy: validation
Validate before calling
// before shipping, assert each option property has at most one CLI annotation
const both = opts.filter(p => p.annotations.flag && p.annotations.arg);
if (both.length) throw new Error(`flag+argument on: ${both.map(p => p.name)}`); Prevention
- Keep exactly one CLI annotation per options-class property
- Delete the old annotation when converting flag <-> argument
- Review annotation blocks after copy-paste
When it happens
Trigger: Annotating one property with both @BooleanFlag/@Flag/@CountedFlag and @Argument in the same options class.
Common situations: Copy-paste of annotation blocks where both annotation lines were left in; refactoring a flag into an argument without deleting the flag annotation.
Understand the failure class
Background: "mutually exclusive" flag errors: what "can't supply both nx and xx", "--raw is not compatible with -i" and "cannot be used with" mean, and how to fix them — this error's family across 29 libraries.
Related errors
- +e.getMessage()
- commandSubcommandConflict
- commandOptionsTypeNotClass
- commandOptionsTypeAbstractClass
- commandArgumentsMultipleRepeated
AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08).
Data as JSON: /api/errors/e91685343771e290.
Report an issue: GitHub.