{"record":{"id":"e46253399e77422a","repo":"quarkusio/quarkus","slug":"annotating-a-method-returning-optional-with-confi","errorCode":null,"errorMessage":"Annotating a method returning Optional with @ConfigProperty and setting defaultValue is not supported. Offending method is  + method.name() +  of interface + ifaceDotName","messagePattern":"Annotating a method returning Optional with @ConfigProperty and setting defaultValue is not supported\\. Offending method is  \\+ method\\.name\\(\\) \\+  of interface \\+ ifaceDotName","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"extensions/spring-boot-properties/deployment/src/main/java/io/quarkus/spring/boot/properties/deployment/InterfaceConfigurationPropertiesUtil.java","lineNumber":212,"sourceCode":"                                    Expr interImpl = bc.new_(\n                                            ConstructorDesc.of(ClassDesc.of(generatedSubInterfaceImp), configClassDesc),\n                                            config);\n                                    bc.return_(interImpl);\n                                    return;\n                                }\n                            }\n\n                            LocalVar config = bc.localVar(\"config\", bc.get(mc.this_().field(configField)));\n                            String defaultValueStr = nameAndDefaultValue.getDefaultValue();\n                            if (DotNames.OPTIONAL.equals(returnType.name())) {\n                                if (defaultValueStr != null) {\n                                    /*\n                                     * it doesn't make to use @ConfigProperty(defaultValue=\"whatever\") on a method that\n                                     * returns\n                                     * Optional\n                                     * since the result in this case isn't \"optional\", but there is always a value\n                                     */\n                                    throw new IllegalArgumentException(\n                                            \"Annotating a method returning Optional with @ConfigProperty and setting defaultValue is not supported. Offending method is \"\n                                                    + method.name() + \" of interface\" + ifaceDotName);\n                                }\n\n                                // use config.getOptionalValue to obtain the result\n\n                                Type genericType = ConfigurationPropertiesUtil.determineSingleGenericType(returnType,\n                                        method.declaringClass().name());\n\n                                if (genericType.kind() != Type.Kind.PARAMETERIZED_TYPE) {\n                                    ConfigurationPropertiesUtil.registerImplicitConverter(genericType, reflectiveClasses);\n                                    Expr result = bc.invokeInterface(\n                                            MethodDesc.of(Config.class, \"getOptionalValue\", Optional.class,\n                                                    String.class,\n                                                    Class.class),\n                                            config, Const.of(fullConfigName),\n                                            bc.classForName(Const.of(genericType.name().toString())));\n                                    bc.return_(result);","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/spring-boot-properties/deployment/src/main/java/io/quarkus/spring/boot/properties/deployment/InterfaceConfigurationPropertiesUtil.java#L194-L230","documentation":"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.","triggerScenarios":"A @ConfigProperties-annotated interface (spring-boot-properties extension) declares a method returning Optional<T> annotated with @ConfigProperty and a non-empty defaultValue attribute.","commonSituations":"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.","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()"],"exampleFix":"// before\n@ConfigProperty(name = \"greeting\", defaultValue = \"hello\")\nOptional<String> greeting();\n// after\n@ConfigProperty(name = \"greeting\")\nOptional<String> greeting();","handlingStrategy":"validation","validationCode":"for (Method m : configIface.getMethods()) {\n    ConfigProperty cp = m.getAnnotation(ConfigProperty.class);\n    if (m.getReturnType() == Optional.class && cp != null && !cp.defaultValue().equals(ConfigProperty.UNSET)) {\n        throw new IllegalStateException(\"Remove defaultValue from Optional-returning \" + m.getName());\n    }\n}","typeGuard":"boolean isOptionalWithDefault(Method m) {\n    ConfigProperty cp = m.getAnnotation(ConfigProperty.class);\n    return m.getReturnType() == Optional.class && cp != null && !cp.defaultValue().equals(ConfigProperty.UNSET);\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["quarkus","config","spring-boot-properties","configproperty"],"backgroundTag":"optional-defaultvalue-unsupported","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}