quarkusio/quarkus · error · IllegalArgumentException
Annotating a method returning Optional with @ConfigProperty
Error message
Annotating a method returning Optional with @ConfigProperty and setting defaultValue is not supported. Offending method is + method.name() + of interface + ifaceDotName
What it means
During build of a Spring Boot style @ConfigProperties interface, Quarkus generates bytecode implementing each getter. A method returning Optional may not carry @ConfigProperty(defaultValue=...) because Quarkus would always produce a value, making the result not truly optional. The deployment fails with IllegalArgumentException.
Source
Thrown at extensions/spring-boot-properties/deployment/src/main/java/io/quarkus/spring/boot/properties/deployment/InterfaceConfigurationPropertiesUtil.java:212
Expr interImpl = bc.new_(
ConstructorDesc.of(ClassDesc.of(generatedSubInterfaceImp), configClassDesc),
config);
bc.return_(interImpl);
return;
}
}
LocalVar config = bc.localVar("config", bc.get(mc.this_().field(configField)));
String defaultValueStr = nameAndDefaultValue.getDefaultValue();
if (DotNames.OPTIONAL.equals(returnType.name())) {
if (defaultValueStr != null) {
/*
* it doesn't make to use @ConfigProperty(defaultValue="whatever") on a method that
* returns
* Optional
* since the result in this case isn't "optional", but there is always a value
*/
throw new IllegalArgumentException(
"Annotating a method returning Optional with @ConfigProperty and setting defaultValue is not supported. Offending method is "
+ method.name() + " of interface" + ifaceDotName);
}
// use config.getOptionalValue to obtain the result
Type genericType = ConfigurationPropertiesUtil.determineSingleGenericType(returnType,
method.declaringClass().name());
if (genericType.kind() != Type.Kind.PARAMETERIZED_TYPE) {
ConfigurationPropertiesUtil.registerImplicitConverter(genericType, reflectiveClasses);
Expr result = bc.invokeInterface(
MethodDesc.of(Config.class, "getOptionalValue", Optional.class,
String.class,
Class.class),
config, Const.of(fullConfigName),
bc.classForName(Const.of(genericType.name().toString())));
bc.return_(result);View on GitHub (pinned to e1c734241f)
Solutions
- Remove the defaultValue attribute from the @ConfigProperty annotation on the Optional-returning method
- Change the method return type from Optional<T> to T and keep defaultValue if a default is needed
- Use Optional<T> without defaultValue; absent config yields Optional.empty()
Example fix
// before @ConfigProperty(name = "greeting", defaultValue = "hello") Optional<String> greeting(); // after @ConfigProperty(name = "greeting") Optional<String> greeting();
Defensive patterns
Strategy: validation
Validate before calling
for (Method m : configIface.getMethods()) {
ConfigProperty cp = m.getAnnotation(ConfigProperty.class);
if (m.getReturnType() == Optional.class && cp != null && !cp.defaultValue().equals(ConfigProperty.UNSET)) {
throw new IllegalStateException("Remove defaultValue from Optional-returning " + m.getName());
}
} Type guard
boolean isOptionalWithDefault(Method m) {
ConfigProperty cp = m.getAnnotation(ConfigProperty.class);
return m.getReturnType() == Optional.class && cp != null && !cp.defaultValue().equals(ConfigProperty.UNSET);
} Prevention
- Never combine Optional return types with defaultValue in @ConfigProperty
- Prefer T with defaultValue when a default exists; Optional without defaultValue when absence matters
- Add an ArchUnit/unit test scanning config interfaces for this pattern
When it happens
Trigger: A @ConfigProperties-annotated interface (spring-boot-properties extension) declares a method returning Optional<T> annotated with @ConfigProperty and a non-empty defaultValue attribute.
Common situations: Migrating Spring @ConfigurationProperties code to Quarkus where getters use Optional as in Spring conventions; adding a defaultValue 'for safety' to an Optional getter during config refactoring.
Related errors
- The class (${name}) cannot be created during deployment.
- Can not add converter ${converter.name()} that is not parame
- Converter ${converter.name()} must be parameterized with a s
- The supplied 'main-class' value of '${mainClassName}' does n
- Unknown class: ${token}
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/e46253399e77422a.
Report an issue: GitHub.