{"record":{"id":"66c5d96e0cea5ac2","repo":"bazelbuild/bazel","slug":"cannot-find-valid-converter-for-option-of-type-s","errorCode":null,"errorMessage":"Cannot find valid converter for option of type %s","messagePattern":"Cannot find valid converter for option of type (.+?)","errorType":"validation","errorClass":"OptionProcessorException","httpStatus":null,"severity":"error","filePath":"src/main/java/com/google/devtools/common/options/processor/OptionsClassProcessor.java","lineNumber":634,"sourceCode":"      Converter<?> converterInstance = findDefaultConverter(acceptedConverterReturnType);\n      if (converterInstance == null) {\n        continue;\n      }\n      try {\n        converterInstance.convert(defaultValue, null);\n      } catch (OptionsParsingException e) {\n        TypeElement converter =\n            elementUtils.getTypeElement(converterInstance.getClass().getCanonicalName());\n        throw new OptionProcessorException(\n            method,\n            e,\n            \"Option lists a default value (%s) that is not parsable by the option's converter (%s)\",\n            defaultValue,\n            converter);\n      }\n      return;\n    }\n    throw new OptionProcessorException(\n        method,\n        \"Cannot find valid converter for option of type %s\",\n        acceptedConverterReturnTypes.get(0));\n  }\n\n  @Nullable\n  private Converter<?> findDefaultConverter(TypeMirror type) {\n    // According to the documentation of TypeMirror, equality check is not how one checks whether\n    // two instances reference the same type but Types.isSameType().\n    for (Map.Entry<TypeMirror, Converter<?>> entry : defaultConverters.entrySet()) {\n      if (typeUtils.isSameType(type, entry.getKey())) {\n        return entry.getValue();\n      }\n    }\n    return null;\n  }\n\n  private void checkProvidedConverter(","sourceCodeStart":616,"sourceCodeEnd":652,"githubUrl":"https://github.com/bazelbuild/bazel/blob/e6e199d0601a244511b4cf18c8b2828aa73db1fd/src/main/java/com/google/devtools/common/options/processor/OptionsClassProcessor.java#L616-L652","documentation":"After failing to match any registered default converter for the option's type (during default-value validation), Bazel's options annotation processor reports this error: no built-in converter exists for the option's return type. Options must either use a type with a known default converter or supply an explicit converter via the @Option converter attribute.","triggerScenarios":"An @Option method whose return type has no entry in the processor's defaultConverters map and no explicit converter attribute — e.g. a custom class like MyCustomType, or a type whose only available converter is not registered with the processor.","commonSituations":"Adding options for domain types (paths, durations with custom formats, enums with special parsing) without writing a Converter; depending on a converter registered at runtime but unknown to the annotation processor; typos in generic element types after an allowMultiple refactor.","solutions":["Write a class implementing Converter<MyType> (convert(String, Object) returning MyType) and reference it: @Option(..., converter = MyTypeConverter.class).","Or change the option's type to one with a default converter (String, boolean, int, long, List<E> of those, etc.).","If a suitable converter already exists in the codebase (search for 'implements Converter'), reuse it via the converter attribute rather than writing a new one.","Recompile to confirm."],"exampleFix":"// before\n@Option(\n  name = \"custom_timeout\",\n  defaultValue = \"30s\"\n)\npublic static DurationSpec customTimeout;  // no default converter\n// after\n@Option(\n  name = \"custom_timeout\",\n  defaultValue = \"30s\",\n  converter = DurationSpecConverter.class\n)\npublic static DurationSpec customTimeout;","handlingStrategy":"validation","validationCode":"// Ensure the option type has a default converter or an explicit one is supplied\nstatic void assertConvertible(TypeMirror optionType,\n                              Map<TypeMirror, Converter<?>> defaultConverters,\n                              Class<? extends Converter<?>> explicitConverter) {\n  boolean hasDefault = defaultConverters.keySet().stream()\n      .anyMatch(t -> t.equals(optionType));\n  Preconditions.checkState(hasDefault || explicitConverter != null,\n      \"No default converter for %s and no converter attribute set\", optionType);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Any option of a custom type must set converter = MyConverter.class in the same commit.","Favor built-in types (String, boolean, int) when the option semantics allow it."],"tags":["java","bazel","annotation-processing","options","compile-time","converter","type-checking"],"backgroundTag":null,"analyzedSha":"e6e199d0601a244511b4cf18c8b2828aa73db1fd","analyzedAt":"2026-08-14T10:24:27.848Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}