{"record":{"id":"1f132567c75a9f76","repo":"bazelbuild/bazel","slug":"option-is-an-expansion-flag-with-a-static-expansio","errorCode":null,"errorMessage":"Option is an expansion flag with a static expansion, but does not have Void type.","messagePattern":"Option is an expansion flag with a static expansion, but does not have Void type\\.","errorType":"validation","errorClass":"OptionProcessorException","httpStatus":null,"severity":"error","filePath":"src/main/java/com/google/devtools/common/options/processor/OptionsClassProcessor.java","lineNumber":546,"sourceCode":"          method, \"Options with metadata tag DEPRECATED must be annotated with @Deprecated.\");\n    }\n    if (hasDeprecatedAnnotation && !hasDeprecatedMetadataTag) {\n      throw new OptionProcessorException(\n          method, \"Options annotated with @Deprecated must have metadata tag DEPRECATED.\");\n    }\n  }\n\n  private void checkConverter(ExecutableElement method) throws OptionProcessorException {\n    TypeMirror optionType = method.getReturnType();\n    Option annotation = method.getAnnotation(Option.class);\n    ImmutableList<TypeMirror> acceptedConverterReturnTypes =\n        getAcceptedConverterReturnTypes(method);\n\n    // For simple, static expansions, don't accept non-Void types.\n    if (annotation.expansion().length != 0\n        && !typeUtils.isSameType(\n            optionType, elementUtils.getTypeElement(Void.class.getCanonicalName()).asType())) {\n      throw new OptionProcessorException(\n          method,\n          \"Option is an expansion flag with a static expansion, but does not have Void type.\");\n    }\n\n    // Obtain the converter for this option.\n    AnnotationMirror optionMirror =\n        ProcessorUtils.getAnnotation(elementUtils, typeUtils, method, Option.class);\n    TypeElement defaultConverterElement =\n        elementUtils.getTypeElement(Converter.class.getCanonicalName());\n    TypeElement converterElement =\n        ProcessorUtils.getClassTypeFromAnnotationField(elementUtils, optionMirror, \"converter\");\n\n    if (typeUtils.isSameType(converterElement.asType(), defaultConverterElement.asType())) {\n      // Find a matching converter in the default converter list, and check that it successfully\n      // parses the default value for this option.\n      checkForDefaultConverter(method, acceptedConverterReturnTypes, annotation.defaultValue());\n    } else {\n      // Check that the provided converter has an accepted return type.","sourceCodeStart":528,"sourceCodeEnd":564,"githubUrl":"https://github.com/bazelbuild/bazel/blob/e6e199d0601a244511b4cf18c8b2828aa73db1fd/src/main/java/com/google/devtools/common/options/processor/OptionsClassProcessor.java#L528-L564","documentation":"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.","triggerScenarios":"@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.","commonSituations":"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.","solutions":["Change the option method's return type to Void (declared as void in the options class).","If the option genuinely needs a value, remove the expansion attribute — a valued option cannot also be a static expansion.","Recompile to confirm."],"exampleFix":"// before\n@Option(\n  name = \"fast_build\",\n  defaultValue = \"false\",\n  expansion = {\"--compilation_mode=fastbuild\"}\n)\npublic static boolean fastBuild;\n// after\n@Option(\n  name = \"fast_build\",\n  defaultValue = \"null\",\n  expansion = {\"--compilation_mode=fastbuild\"}\n)\npublic static void fastBuild;","handlingStrategy":"type-guard","validationCode":"static void checkExpansionIsVoid(String[] expansion, Class<?> optionType) {\n  if (expansion.length > 0) {\n    Preconditions.checkState(optionType == Void.class,\n        \"Static expansion options must be Void, got %s\", optionType);\n  }\n}","typeGuard":"// Ensure expansion options are declared Void before registration\nstatic boolean isValidExpansionOption(String[] expansion, java.lang.reflect.Type type) {\n  return expansion.length == 0 || type == Void.class;\n}","tryCatchPattern":null,"preventionTips":["Convention: every static expansion option is declared 'public static void' with defaultValue \"null\".","Copy an existing, known-good expansion option as your template."],"tags":["java","bazel","annotation-processing","options","compile-time","expansion","type-checking"],"backgroundTag":null,"analyzedSha":"e6e199d0601a244511b4cf18c8b2828aa73db1fd","analyzedAt":"2026-08-14T10:24:27.848Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}