{"record":{"id":"130a7f263798840f","repo":"bazelbuild/bazel","slug":"option-that-allows-multiple-occurrences-must-be-as","errorCode":null,"errorMessage":"Option that allows multiple occurrences must be assignable to type %s, but is of type %s","messagePattern":"Option that allows multiple occurrences must be assignable to type (.+?), but is of type (.+?)","errorType":"validation","errorClass":"OptionProcessorException","httpStatus":null,"severity":"error","filePath":"src/main/java/com/google/devtools/common/options/processor/OptionsClassProcessor.java","lineNumber":585,"sourceCode":"  }\n\n  private ImmutableList<TypeMirror> getAcceptedConverterReturnTypes(ExecutableElement method)\n      throws OptionProcessorException {\n    TypeMirror optionType = method.getReturnType();\n    Option annotation = method.getAnnotation(Option.class);\n    TypeMirror listType = elementUtils.getTypeElement(List.class.getCanonicalName()).asType();\n\n    if (annotation.allowMultiple()) {\n      if (optionType.getKind() != TypeKind.DECLARED) {\n        throw new OptionProcessorException(\n            method,\n            \"Option that allows multiple occurrences must be of type %s, but is of type %s\",\n            listType,\n            optionType);\n      }\n      DeclaredType optionDeclaredType = (DeclaredType) optionType;\n      if (!typeUtils.isAssignable(typeUtils.erasure(optionDeclaredType), listType)) {\n        throw new OptionProcessorException(\n            method,\n            \"Option that allows multiple occurrences must be assignable to type %s, but is of type\"\n                + \" %s\",\n            listType,\n            optionType);\n      }\n      List<? extends TypeMirror> genericParameters = optionDeclaredType.getTypeArguments();\n      if (genericParameters.size() != 1) {\n        throw new OptionProcessorException(\n            method,\n            \"Option that allows multiple occurrences must be of type %s, where E is the type of an\"\n                + \" individual command-line mention of this option, but is of type %s\",\n            listType,\n            optionType);\n      }\n      return ImmutableList.of(genericParameters.get(0), optionType);\n    } else {\n      return ImmutableList.of(optionType);","sourceCodeStart":567,"sourceCodeEnd":603,"githubUrl":"https://github.com/bazelbuild/bazel/blob/e6e199d0601a244511b4cf18c8b2828aa73db1fd/src/main/java/com/google/devtools/common/options/processor/OptionsClassProcessor.java#L567-L603","documentation":"The second shape check for allowMultiple options in Bazel's options annotation processor: the option's declared type must be assignable to java.util.List after erasure. Types that are declared but not List-compatible (e.g. Set, Map, Collection, or a custom class) are rejected, because the parser accumulates repeated occurrences into a List.","triggerScenarios":"@Option(allowMultiple = true) on a method whose type passes the DECLARED check but whose erasure is not assignable to List — e.g. Set<String>, Map<String, String>, Collection<String>, or ImmutableList via a non-assignable declaration.","commonSituations":"Wanting de-duplication and reaching for Set; modeling key=value repeats as Map; declaring the field as a custom accumulator type. All are disallowed — the canonical type is List.","solutions":["Change the option type to List<E> (ArrayList-style declaration), e.g. List<String>.","If Set or Map semantics are needed, take List<E> in the option and convert in the code that reads it (or in a converter).","Recompile to confirm."],"exampleFix":"// before\n@Option(\n  name = \"platform\",\n  defaultValue = \"null\",\n  allowMultiple = true\n)\npublic static Set<String> platform;\n// after\n@Option(\n  name = \"platform\",\n  defaultValue = \"null\",\n  allowMultiple = true\n)\npublic static List<String> platform;","handlingStrategy":"type-guard","validationCode":"static void checkMultipleIsList(boolean allowMultiple, TypeMirror t, Types types,\n                                Elements elements) {\n  if (allowMultiple) {\n    TypeMirror listType = elements.getTypeElement(\"java.util.List\").asType();\n    Preconditions.checkState(types.isAssignable(types.erasure(t), listType),\n        \"allowMultiple options must be assignable to List, got %s\", t);\n  }\n}","typeGuard":"static boolean isListBackedRepeatable(Class<?> clazz) {\n  return List.class.isAssignableFrom(clazz);\n}","tryCatchPattern":null,"preventionTips":["Use java.util.List (or an assignable subtype) as the declared type; Set/Map/Collection are rejected.","Translate to Set/Map after parsing, in consuming code."],"tags":["java","bazel","annotation-processing","options","compile-time","allow-multiple","type-checking","generics"],"backgroundTag":null,"analyzedSha":"e6e199d0601a244511b4cf18c8b2828aa73db1fd","analyzedAt":"2026-08-14T10:24:27.848Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}