bazelbuild/bazel · error · OptionProcessorException

@Option method must be abstract

Error message

@Option method must be abstract

What it means

The options annotation processor requires @Option-annotated interface methods to be abstract — no default bodies — because the processor generates the implementation itself. A method with a default implementation (or otherwise non-abstract) fails this check at compile time immediately after the visibility check.

Source

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

              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);
    checkMetadataTagAndCategoryRationality(method);
    checkNoDefaultValueForMultipleOption(method);
    checkDeprecated(method);

View on GitHub (pinned to e6e199d060)

Solutions

  1. Remove the method body and the default keyword, leaving only the signature
  2. Express the default via @Option(defaultValue = ...) on the annotation instead
  3. Recompile to confirm the processor accepts the interface

Example fix

// before
@Option(name = "foo", defaultValue = "x", effectTags = {OptionEffectTag.NO_OP})
default String getFoo() { return "x"; }
// after
@Option(name = "foo", defaultValue = "x", effectTags = {OptionEffectTag.NO_OP})
String getFoo();
Defensive patterns

Strategy: validation

Validate before calling

// Fail tests that find default bodies on @Option methods
for (var m : MyOptions.class.getMethods()) {
  if (m.isAnnotationPresent(Option.class) && !Modifier.isAbstract(m.getModifiers())) {
    throw new AssertionError("@Option method must be abstract: " + m);
  }
}

Prevention

When it happens

Trigger: Giving an @Option interface method a default body: `default String getFoo() { return "x"; }`, or converting an existing interface where the getter already has a default.

Common situations: Refactoring old option classes where defaults lived in getters; IDE 'extract interface' producing default methods; copy-pasting a concrete getter into a new options interface.

Related errors


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