quarkusio/quarkus · error · IllegalArgumentException

Nested configuration class ' + fieldTypeClassInfo + ' must b

Error message

Nested configuration class ' + fieldTypeClassInfo + ' must be public 

What it means

A field of the configuration properties class is itself a nested configuration object. Nested classes must be instantiable reflectively, and Quarkus requires them to be public; a non-public nested configuration type fails the build.

Source

Thrown at extensions/spring-boot-properties/deployment/src/main/java/io/quarkus/spring/boot/properties/deployment/ClassConfigurationPropertiesUtil.java:331

                        // we need to register the 'valueOf' method of the enum for reflection because
                        // that is the method that SmallryeConfig uses for conversion of enums
                        List<MethodInfo> methods = fieldTypeClassInfo.methods();
                        for (MethodInfo method : methods) {
                            if (!method.name().equals("valueOf")) {
                                continue;
                            }
                            if (method.parametersCount() != 1) {
                                continue;
                            }
                            if (method.parameterType(0).name().equals(DotNames.STRING)) {
                                reflectiveMethods.produce(new ReflectiveMethodBuildItem(getClass().getName(), method));
                                break;
                            }
                        }
                    } else if (fieldTypeClassInfo.hasNoArgsConstructor()) {
                        if (!Modifier.isPublic(fieldTypeClassInfo.flags())) {
                            throw new IllegalArgumentException(
                                    "Nested configuration class '" + fieldTypeClassInfo + "' must be public ");
                        }

                        Expr nestedConfigObject = populateConfigObject(classLoader, fieldTypeClassInfo,
                                getFullConfigName(prefixStr, namingStrategy, field), namingStrategy, failOnMismatchingMember,
                                null, bc, configParam);
                        createWriteValue(bc, configObject, field, setter, useFieldAccess, nestedConfigObject);
                    } else {
                        LOGGER.warn("Nested configuration class '" + fieldTypeClassInfo
                                + "' declared in '" + currentClassInHierarchy.name() + "." + field.name() + "' is either an "
                                + "interface or does not have a non-args constructor, so this field will not be initialized");
                    }
                } else {
                    String fullConfigName = getFullConfigName(prefixStr, namingStrategy, field);
                    if (DotNames.OPTIONAL.equals(fieldTypeDotName)) {
                        Type genericType = ConfigurationPropertiesUtil.determineSingleGenericType(field.type(),
                                field.declaringClass().name());

View on GitHub (pinned to e1c734241f)

Solutions

  1. Make the nested configuration class public.
  2. If it is an inner class, make it a public static nested class or a public top-level class.
  3. Flatten the nested fields into the parent class if nesting is not required.

Example fix

// before
public class AppProps {
    class Security { private boolean enabled; }
}
// after
public class AppProps {
    public static class Security { private boolean enabled;
        public boolean isEnabled() { return enabled; }
        public void setEnabled(boolean e) { enabled = e; } }
}
Defensive patterns

Strategy: validation

Validate before calling

static void checkNestedConfigPublic(Class<?> props) {
    for (Field f : props.getDeclaredFields()) {
        Class<?> t = f.getType();
        if (!t.isPrimitive() && !t.getName().startsWith("java.") && !Modifier.isPublic(t.getModifiers()))
            throw new IllegalArgumentException("Nested config class " + t + " must be public");
    }
}

Prevention

When it happens

Trigger: populateConfigObject encounters a field whose type (fieldTypeClassInfo) has a no-arg constructor and is treated as a nested config object; Modifier.isPublic(fieldTypeClassInfo.flags()) is false, so it throws before recursing into the nested population.

Common situations: Package-private inner classes grouping related properties (e.g. AppProps.Security) in migrated Spring Boot apps; default (package) visibility on nested POJOs.

Related errors


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