bazelbuild/bazel · error · OptionProcessorException

Option is an expansion flag with a static expansion, but doe

Error message

Option is an expansion flag with a static expansion, but does not have Void type.

What it means

Bazel's options annotation processor requires that an option with a static expansion (a non-empty expansion attribute) have return type Void. Expansion options exist purely to rewrite the command line; they carry no value of their own, so a non-Void return type signals a modeling mistake.

Source

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

          method, "Options with metadata tag DEPRECATED must be annotated with @Deprecated.");
    }
    if (hasDeprecatedAnnotation && !hasDeprecatedMetadataTag) {
      throw new OptionProcessorException(
          method, "Options annotated with @Deprecated must have metadata tag DEPRECATED.");
    }
  }

  private void checkConverter(ExecutableElement method) throws OptionProcessorException {
    TypeMirror optionType = method.getReturnType();
    Option annotation = method.getAnnotation(Option.class);
    ImmutableList<TypeMirror> acceptedConverterReturnTypes =
        getAcceptedConverterReturnTypes(method);

    // For simple, static expansions, don't accept non-Void types.
    if (annotation.expansion().length != 0
        && !typeUtils.isSameType(
            optionType, elementUtils.getTypeElement(Void.class.getCanonicalName()).asType())) {
      throw new OptionProcessorException(
          method,
          "Option is an expansion flag with a static expansion, but does not have Void type.");
    }

    // Obtain the converter for this option.
    AnnotationMirror optionMirror =
        ProcessorUtils.getAnnotation(elementUtils, typeUtils, method, Option.class);
    TypeElement defaultConverterElement =
        elementUtils.getTypeElement(Converter.class.getCanonicalName());
    TypeElement converterElement =
        ProcessorUtils.getClassTypeFromAnnotationField(elementUtils, optionMirror, "converter");

    if (typeUtils.isSameType(converterElement.asType(), defaultConverterElement.asType())) {
      // Find a matching converter in the default converter list, and check that it successfully
      // parses the default value for this option.
      checkForDefaultConverter(method, acceptedConverterReturnTypes, annotation.defaultValue());
    } else {
      // Check that the provided converter has an accepted return type.

View on GitHub (pinned to e6e199d060)

Solutions

  1. Change the option method's return type to Void (declared as void in the options class).
  2. If the option genuinely needs a value, remove the expansion attribute — a valued option cannot also be a static expansion.
  3. Recompile to confirm.

Example fix

// before
@Option(
  name = "fast_build",
  defaultValue = "false",
  expansion = {"--compilation_mode=fastbuild"}
)
public static boolean fastBuild;
// after
@Option(
  name = "fast_build",
  defaultValue = "null",
  expansion = {"--compilation_mode=fastbuild"}
)
public static void fastBuild;
Defensive patterns

Strategy: type-guard

Validate before calling

static void checkExpansionIsVoid(String[] expansion, Class<?> optionType) {
  if (expansion.length > 0) {
    Preconditions.checkState(optionType == Void.class,
        "Static expansion options must be Void, got %s", optionType);
  }
}

Type guard

// Ensure expansion options are declared Void before registration
static boolean isValidExpansionOption(String[] expansion, java.lang.reflect.Type type) {
  return expansion.length == 0 || type == Void.class;
}

Prevention

When it happens

Trigger: @Option(..., expansion = {"--foo=1"}) on a method whose return type is anything other than Void (e.g. Boolean, String, Integer) or void (primitive) — checked with Types.isSameType against java.lang.Void.

Common situations: Turning a valued flag into an alias for other flags while keeping its old type; writing a new expansion option by copying a Boolean-valued option; forgetting that expansion options are conventionally defined as 'public static void' accessor methods with Void in the abstract-option paradigm.

Related errors


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