bazelbuild/bazel · error · OptionProcessorException

Options annotated with @Deprecated must have metadata tag…

Error message

Options annotated with @Deprecated must have metadata tag DEPRECATED.

What it means

The mirror of the previous check in Bazel's options annotation processor: an @Option method annotated with java.lang.@Deprecated must also declare OptionMetadataTag.DEPRECATED in metadataTags, so that Bazel's generated documentation marks the flag as deprecated consistently with the Java API.

Solutions

  1. Add OptionMetadataTag.DEPRECATED to the method's metadataTags array.
  2. If the deprecation itself was accidental, remove @Deprecated from the method instead.
  3. Recompile to confirm.

Example fix

// before
@Deprecated
@Option(
  name = "legacy_path",
  defaultValue = "null",
  effectTags = {OptionEffectTag.EAGER}
)
// after
@Deprecated
@Option(
  name = "legacy_path",
  defaultValue = "null",
  effectTags = {OptionEffectTag.EAGER},
  metadataTags = {OptionMetadataTag.DEPRECATED}
)
Defensive patterns

Strategy: validation

Validate before calling

static void checkAnnotationMatchesDeprecatedTag(boolean hasDeprecatedAnnotation,
                                                     boolean hasDeprecatedTag) {
  Preconditions.checkState(!hasDeprecatedAnnotation || hasDeprecatedTag,
      "@Deprecated options must add metadataTags DEPRECATED");
}

Prevention

When it happens

Trigger: A method carrying both @Option and @Deprecated whose metadataTags attribute does not include OptionMetadataTag.DEPRECATED.

Common situations: Applying @Deprecated to an options class or method during ordinary Java cleanup without updating the @Option metadata; deprecating an option for removal but wanting it to stay documented (the tag, not category, is how deprecation is expressed now).

Related errors


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

Appendix: source

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

      // Allowlist for tests - these are in the process of being fixed.
      String enclosingClassName = method.getEnclosingElement().toString();
      boolean allowlisted =
          NO_OP_OPTION_ALLOWLIST.stream().anyMatch(enclosingClassName::startsWith);
      if (!allowlisted) {
        throw new OptionProcessorException(
            method,
            "No-op options must be annotated with @Deprecated, or have metadata tag HIDDEN or"
                + " INTERNAL. Alternatively add %s to the allowlist.",
            enclosingClassName);
      }
    }

    if (hasDeprecatedMetadataTag && !hasDeprecatedAnnotation) {
      throw new OptionProcessorException(
          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.");
    }

View on GitHub (pinned to e6e199d060)