{"record":{"id":"e91685343771e290","repo":"apple/pkl","slug":"commandoptionbothflagandargument","errorCode":"commandOptionBothFlagAndArgument","errorMessage":"commandOptionBothFlagAndArgument","messagePattern":"commandOptionBothFlagAndArgument","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkl-core/src/main/java/org/pkl/core/runtime/CommandSpecParser.java","lineNumber":217,"sourceCode":"      for (var prop : clazz.getDeclaredProperties()) {\n        var name = prop.getName().toString();\n        if (VmModifier.isLocalOrExternalOrAbstractOrFixedOrConst(prop.getModifiers())\n            || opts.containsKey(name)) continue;\n\n        VmTyped flagAnnotation = null;\n        VmTyped argAnnotation = null;\n        for (var annotation : prop.getAllAnnotations(true)) {\n          if (annotation.getVmClass().isSubclassOf(CommandModule.getBaseFlagClass())) {\n            if (flagAnnotation != null) continue;\n            flagAnnotation = annotation;\n          } else if (annotation.getVmClass() == CommandModule.getArgumentClass()) {\n            if (argAnnotation != null) continue;\n            argAnnotation = annotation;\n          }\n        }\n\n        if (flagAnnotation != null && argAnnotation != null) {\n          throw exceptionBuilder()\n              .withSourceSection(prop.getHeaderSection())\n              .evalError(\"commandOptionBothFlagAndArgument\", prop.getName())\n              .build();\n        }\n\n        if (argAnnotation != null) {\n          var arg = collectArgument(prop, argAnnotation);\n          opts.put(arg.name(), arg);\n          if (arg.repeated()) {\n            if (lastRepeatedArg == null) {\n              lastRepeatedArg = arg;\n            } else {\n              throw exceptionBuilder()\n                  .withSourceSection(optionsClass.getHeaderSection())\n                  .evalError(\"commandArgumentsMultipleRepeated\", lastRepeatedArg.name(), arg.name())\n                  .build();\n            }\n          }","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-core/src/main/java/org/pkl/core/runtime/CommandSpecParser.java#L199-L235","documentation":"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.","triggerScenarios":"Annotating one property with both @BooleanFlag/@Flag/@CountedFlag and @Argument in the same options class.","commonSituations":"Copy-paste of annotation blocks where both annotation lines were left in; refactoring a flag into an argument without deleting the flag annotation.","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"],"exampleFix":"// before\n@BooleanFlag { name = \"verbose\" }\n@Argument\nverbose: Boolean\n// after\n@BooleanFlag { name = \"verbose\" }\nverbose: Boolean","handlingStrategy":"validation","validationCode":"// before shipping, assert each option property has at most one CLI annotation\nconst both = opts.filter(p => p.annotations.flag && p.annotations.arg);\nif (both.length) throw new Error(`flag+argument on: ${both.map(p => p.name)}`);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep exactly one CLI annotation per options-class property","Delete the old annotation when converting flag <-> argument","Review annotation blocks after copy-paste"],"tags":["pkl","cli","command-spec","annotations"],"backgroundTag":"mutually-exclusive-flags","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}