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

  1. Remove the defaultValue attribute from the @ConfigProperty annotation on the Optional-returning method
  2. Change the method return type from Optional<T> to T and keep defaultValue if a default is needed
  3. 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

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


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/e46253399e77422a. Report an issue: GitHub.