bazelbuild/bazel · error · OptionProcessorException

The converter type %s must be a concrete type

Error message

The converter type %s must be a concrete type

What it means

When an @Option specifies a custom converter class, Bazel's options annotation processor verifies the converter is instantiable. This error fires when the supplied converter class is abstract: the processor cannot instantiate it to call convert(), so the option definition is rejected at compile time.

Source

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

  @Nullable
  private Converter<?> findDefaultConverter(TypeMirror type) {
    // According to the documentation of TypeMirror, equality check is not how one checks whether
    // two instances reference the same type but Types.isSameType().
    for (Map.Entry<TypeMirror, Converter<?>> entry : defaultConverters.entrySet()) {
      if (typeUtils.isSameType(type, entry.getKey())) {
        return entry.getValue();
      }
    }
    return null;
  }

  private void checkProvidedConverter(
      ExecutableElement method,
      ImmutableList<TypeMirror> acceptedConverterReturnTypes,
      TypeElement converterElement)
      throws OptionProcessorException {
    if (converterElement.getModifiers().contains(Modifier.ABSTRACT)) {
      throw new OptionProcessorException(
          method, "The converter type %s must be a concrete type", converterElement.asType());
    }

    DeclaredType converterType = (DeclaredType) converterElement.asType();
    List<ExecutableElement> methodList =
        elementUtils.getAllMembers(converterElement).stream()
            .filter(element -> element.getKind() == ElementKind.METHOD)
            .map(methodElement -> (ExecutableElement) methodElement)
            .filter(methodElement -> methodElement.getSimpleName().contentEquals("convert"))
            .filter(
                methodElement ->
                    methodElement.getParameters().size() == 2
                        && typeUtils.isSameType(
                            methodElement.getParameters().get(0).asType(),
                            elementUtils.getTypeElement(String.class.getCanonicalName()).asType())
                        && typeUtils.isSameType(
                            methodElement.getParameters().get(1).asType(),
                            elementUtils.getTypeElement(Object.class.getCanonicalName()).asType()))

View on GitHub (pinned to e6e199d060)

Solutions

  1. Reference the concrete subclass in the converter attribute: converter = ConcreteConverter.class.
  2. If the abstract class is the only thing that exists, implement it as a concrete class (or make it a final utility class implementing Converter directly).
  3. Recompile to confirm.

Example fix

// before
abstract class TimeoutConverter implements Converter<Duration> { ... }

@Option(
  name = "timeout",
  defaultValue = "30s",
  converter = TimeoutConverter.class  // abstract -> error
)
// after
final class TimeoutConverter implements Converter<Duration> { ... }

@Option(
  name = "timeout",
  defaultValue = "30s",
  converter = TimeoutConverter.class
)
Defensive patterns

Strategy: type-guard

Validate before calling

static void assertConverterConcrete(Class<? extends Converter<?>> converter) {
  Preconditions.checkState(!Modifier.isAbstract(converter.getModifiers()),
        "Converter %s must be concrete (non-abstract)", converter.getName());
}

Type guard

static boolean isInstantiableConverter(Class<?> c) {
  return Converter.class.isAssignableFrom(c)
      && !c.isInterface()
      && !Modifier.isAbstract(c.getModifiers());
}

Prevention

When it happens

Trigger: @Option(..., converter = SomeAbstractConverter.class) where SomeAbstractConverter is declared abstract (or is an interface referenced as a converter).

Common situations: Pointing at a converter base class instead of a concrete subclass; introducing a generic converter hierarchy and wiring the base type by mistake; refactoring a converter into abstract+concrete pair and forgetting to update the @Option reference.

Related errors


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