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.

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)

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.