bazelbuild/bazel · error · OptionProcessorException
No-op options must be annotated with @Deprecated, or have me
Error message
No-op options must be annotated with @Deprecated, or have metadata tag HIDDEN or INTERNAL. Alternatively add %s to the allowlist.
What it means
This compile-time error enforces Bazel's policy that no-op options (effectTags contains NO_OP) must not look like live, supported flags. A NO_OP option must additionally be marked @Deprecated, or carry metadata tag HIDDEN or INTERNAL, or its enclosing class must be listed in the processor's temporary NO_OP_OPTION_ALLOWLIST (test classes being fixed). Otherwise the processor rejects it.
Source
Thrown at src/main/java/com/google/devtools/common/options/processor/OptionsClassProcessor.java:518
"com.google.devtools.common.options.TestOptions");
private void checkDeprecated(ExecutableElement method) throws OptionProcessorException {
Option annotation = method.getAnnotation(Option.class);
ImmutableList<OptionEffectTag> effectTags = ImmutableList.copyOf(annotation.effectTags());
ImmutableList<OptionMetadataTag> metadataTags = ImmutableList.copyOf(annotation.metadataTags());
boolean hasDeprecatedAnnotation = method.getAnnotation(Deprecated.class) != null;
boolean hasDeprecatedMetadataTag = metadataTags.contains(OptionMetadataTag.DEPRECATED);
if (effectTags.contains(OptionEffectTag.NO_OP)
&& !metadataTags.contains(OptionMetadataTag.HIDDEN)
&& !metadataTags.contains(OptionMetadataTag.INTERNAL)
&& !hasDeprecatedAnnotation) {
// 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 {View on GitHub (pinned to e6e199d060)
Solutions
- If the option is being retired, add @Deprecated to the method AND metadataTags = {OptionMetadataTag.DEPRECATED} (both are required by the paired checks).
- If the option is intentionally hidden infrastructure, add OptionMetadataTag.HIDDEN or OptionMetadataTag.INTERNAL and set documentationCategory = UNDOCUMENTED.
- If the option is supposed to actually do something, replace NO_OP with a real effect tag such as EAGER.
- For test classes genuinely needing an unannotated no-op, add the fully-qualified enclosing class name plus trailing dot to NO_OP_OPTION_ALLOWLIST in OptionsClassProcessor.java — as a last resort, since the allowlist is scheduled for removal.
Example fix
// before
@Option(
name = "stub_flag",
defaultValue = "false",
documentationCategory = OptionDocumentationCategory.MISC,
effectTags = {OptionEffectTag.NO_OP}
)
// after
@Option(
name = "stub_flag",
defaultValue = "false",
documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
effectTags = {OptionEffectTag.NO_OP},
metadataTags = {OptionMetadataTag.DEPRECATED}
)
@Deprecated Defensive patterns
Strategy: validation
Validate before calling
static void checkNoOpIsMarked(boolean isNoOp,
boolean hidden, boolean internal,
boolean deprecated,
String enclosingClass) {
if (isNoOp && !hidden && !internal && !deprecated) {
Preconditions.checkState(NO_OP_OPTION_ALLOWLIST.stream().anyMatch(enclosingClass::startsWith),
"Unmarked no-op option in %s: add @Deprecated or HIDDEN/INTERNAL", enclosingClass);
}
} Prevention
- New no-op flags: add @Deprecated plus metadataTags DEPRECATED, or HIDDEN/INTERNAL with UNDOCUMENTED category, from day one.
- Do not grow the allowlist for production code; it exists only for in-flight test fixes.
When it happens
Trigger: An @Option method with effectTags = {OptionEffectTag.NO_OP} (alone or otherwise), whose class lacks @Deprecated, whose metadataTags contain neither HIDDEN nor INTERNAL, and whose fully-qualified enclosing class name does not start with one of the allowlist prefixes (e.g. com.google.devtools.build.lib.analysis.AnalysisCachingTest.).
Common situations: Introducing a new flag stubbed to do nothing while developing a feature; neutralizing a flag's implementation but leaving it publicly documented; upstream policy tightening (the allowlist is explicitly temporary, so previously-passing test classes can start failing when the allowlist shrinks).
Related errors
- Option includes NO_OP with other effects. This doesn't make
- Options with metadata tag DEPRECATED must be annotated with
- Options annotated with @Deprecated must have metadata tag DE
- Option includes UNKNOWN with other, known, effects. Please r
- Option has metadata tag %s but does not have category UNDOCU
AI-assisted analysis of bazelbuild/bazel@e6e199d060 (2026-08-14).
Data as JSON: /api/errors/b3764e799d932478.
Report an issue: GitHub.