bazelbuild/bazel · error · OptionProcessorException

@Option method must be public

Error message

@Option method must be public

What it means

OptionsClassProcessor is the annotation processor behind @Option-annotated interfaces. When an @Option-annotated method (the interface-method style introduced for BoM-style option interfaces) lacks the public modifier, processing aborts with this compile-time error pointing at the method. The processor must be able to generate an implementing class that overrides the method, which requires public visibility.

Solutions

  1. Mark the annotated method public
  2. Keep the containing interface public too, since generated implementations live elsewhere
  3. Rebuild; the processor re-runs and should proceed past this check

Example fix

// before
public interface MyOptions {
  @Option(name = "foo", effectTags = {OptionEffectTag.NO_OP})
  String getFoo();
}
// after
public interface MyOptions {
  @Option(name = "foo", effectTags = {OptionEffectTag.NO_OP})
  public String getFoo();
}
Defensive patterns

Strategy: validation

Validate before calling

// Reflection-style pre-check in tests for options interfaces
for (var m : MyOptions.class.getMethods()) {
  if (m.isAnnotationPresent(Option.class)
      && !java.lang.reflect.Modifier.isPublic(m.getModifiers())) {
    throw new AssertionError("@Option method not public: " + m);
  }
}

Prevention

When it happens

Trigger: Declaring an @Option method in an interface without public — e.g. a package-private or default-visibility getter — and compiling with the options annotation processor on the classpath.

Common situations: Writing a new BoM/options interface by hand; refactoring a class with package-private option fields into interface methods; IDE auto-generating getters with default visibility.

Related errors


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

Appendix: source

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

              """,
              option.fieldType,
              fieldName,
              option.capitalizedFieldName,
              option.hasSetterInBase ? "@Override" : "");
        }

        out.println("}");
      }
    } catch (IOException e) {
      messager.printMessage(
          Diagnostic.Kind.ERROR,
          "Failed to generate implementation for " + className + ": " + e.getMessage());
    }
  }

  private void checkMethodOption(ExecutableElement method) throws OptionProcessorException {
    if (!method.getModifiers().contains(Modifier.PUBLIC)) {
      throw new OptionProcessorException(method, "@Option method must be public");
    }
    if (!method.getModifiers().contains(Modifier.ABSTRACT)) {
      throw new OptionProcessorException(method, "@Option method must be abstract");
    }

    String methodName = method.getSimpleName().toString();
    if (!methodName.startsWith("get")
        || methodName.length() < 4
        || !Character.isUpperCase(methodName.charAt(3))) {
      throw new OptionProcessorException(
          method, "Annotated method name must start with 'get' followed by an uppercase letter");
    }

    checkOptionName(method);
    checkOldCategoriesAreNotUsed(method);
    checkExpansionOptions(method);
    checkConverter(method);
    checkEffectTagRationality(method);

View on GitHub (pinned to e6e199d060)