bazelbuild/bazel · error · OptionProcessorException

Option that allows multiple occurrences must be assignable t

Error message

Option that allows multiple occurrences must be assignable to type %s, but is of type %s

What it means

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.

Source

Thrown at src/main/java/com/google/devtools/common/options/processor/OptionsClassProcessor.java:585

  }

  private ImmutableList<TypeMirror> getAcceptedConverterReturnTypes(ExecutableElement method)
      throws OptionProcessorException {
    TypeMirror optionType = method.getReturnType();
    Option annotation = method.getAnnotation(Option.class);
    TypeMirror listType = elementUtils.getTypeElement(List.class.getCanonicalName()).asType();

    if (annotation.allowMultiple()) {
      if (optionType.getKind() != TypeKind.DECLARED) {
        throw new OptionProcessorException(
            method,
            "Option that allows multiple occurrences must be of type %s, but is of type %s",
            listType,
            optionType);
      }
      DeclaredType optionDeclaredType = (DeclaredType) optionType;
      if (!typeUtils.isAssignable(typeUtils.erasure(optionDeclaredType), listType)) {
        throw new OptionProcessorException(
            method,
            "Option that allows multiple occurrences must be assignable to type %s, but is of type"
                + " %s",
            listType,
            optionType);
      }
      List<? extends TypeMirror> genericParameters = optionDeclaredType.getTypeArguments();
      if (genericParameters.size() != 1) {
        throw new OptionProcessorException(
            method,
            "Option that allows multiple occurrences must be of type %s, where E is the type of an"
                + " individual command-line mention of this option, but is of type %s",
            listType,
            optionType);
      }
      return ImmutableList.of(genericParameters.get(0), optionType);
    } else {
      return ImmutableList.of(optionType);

View on GitHub (pinned to e6e199d060)

Solutions

  1. Change the option type to List<E> (ArrayList-style declaration), e.g. List<String>.
  2. 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).
  3. Recompile to confirm.

Example fix

// before
@Option(
  name = "platform",
  defaultValue = "null",
  allowMultiple = true
)
public static Set<String> platform;
// after
@Option(
  name = "platform",
  defaultValue = "null",
  allowMultiple = true
)
public static List<String> platform;
Defensive patterns

Strategy: type-guard

Validate before calling

static void checkMultipleIsList(boolean allowMultiple, TypeMirror t, Types types,
                                Elements elements) {
  if (allowMultiple) {
    TypeMirror listType = elements.getTypeElement("java.util.List").asType();
    Preconditions.checkState(types.isAssignable(types.erasure(t), listType),
        "allowMultiple options must be assignable to List, got %s", t);
  }
}

Type guard

static boolean isListBackedRepeatable(Class<?> clazz) {
  return List.class.isAssignableFrom(clazz);
}

Prevention

When it happens

Trigger: @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.

Common situations: 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.

Related errors


AI-assisted analysis of bazelbuild/bazel@e6e199d060 (2026-08-14). Data as JSON: /api/errors/130a7f263798840f. Report an issue: GitHub.