{"record":{"id":"5f3ed12fd5d7c810","repo":"quarkusio/quarkus","slug":"the-configuration-clazz-must-be-an-interface-an","errorCode":null,"errorMessage":"The configuration ${clazz} must be an interface annotated with @ConfigRoot and @ConfigMapping","messagePattern":"The configuration (.+?) must be an interface annotated with @ConfigRoot and @ConfigMapping","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"core/deployment/src/main/java/io/quarkus/deployment/configuration/BuildTimeConfigurationReader.java","lineNumber":59,"sourceCode":"import io.smallrye.config.SmallRyeConfig;\nimport io.smallrye.config.SmallRyeConfigBuilder;\nimport io.smallrye.config.SmallRyeConfigBuilderCustomizer;\nimport io.smallrye.config.SysPropConfigSource;\nimport io.smallrye.config.common.AbstractConfigSource;\n\n/**\n * A configuration reader.\n */\npublic final class BuildTimeConfigurationReader {\n    private static final String CONFIG_ROOTS_LIST = \"META-INF/quarkus-config-roots.list\";\n\n    private static List<Class<?>> collectConfigRoots(ClassLoader classLoader) throws IOException, ClassNotFoundException {\n        Assert.checkNotNullParam(\"classLoader\", classLoader);\n        // populate with all known types\n        List<Class<?>> roots = new ArrayList<>();\n        for (Class<?> clazz : ServiceUtil.classesNamedIn(classLoader, CONFIG_ROOTS_LIST)) {\n            if (!clazz.isInterface()) {\n                throw new IllegalArgumentException(\n                        \"The configuration \" + clazz + \" must be an interface annotated with @ConfigRoot and @ConfigMapping\");\n            }\n\n            ConfigRoot configRoot = clazz.getAnnotation(ConfigRoot.class);\n            if (configRoot == null) {\n                throw new IllegalArgumentException(\"The configuration \" + clazz + \" is missing the @ConfigRoot annotation\");\n            }\n\n            ConfigMapping configMapping = clazz.getAnnotation(ConfigMapping.class);\n            if (configMapping == null) {\n                throw new IllegalArgumentException(\"The configuration \" + clazz + \" is missing the @ConfigMapping annotation\");\n            }\n\n            roots.add(clazz);\n        }\n        return roots;\n    }\n","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/core/deployment/src/main/java/io/quarkus/deployment/configuration/BuildTimeConfigurationReader.java#L41-L77","documentation":"BuildTimeConfigurationReader.collectConfigRoots loads classes listed in the generated config-roots list and requires each to be an interface annotated with @ConfigRoot (and @ConfigMapping). A non-interface class in that list violates the configuration model contract, so IllegalArgumentException is thrown at build start.","triggerScenarios":"A class implementing/registered as a config root that is a class or enum rather than an interface; stale generated CONFIG_ROOTS_LIST service file referencing a legacy class-based config root.","commonSituations":"Migrating from legacy @ConfigRoot class + @ConfigItem style (removed in modern Quarkus) to @ConfigMapping interfaces; a custom extension still registering an old-style config class.","solutions":["Convert the config root to a Java interface annotated with @ConfigMapping and @ConfigRoot","Remove any leftover legacy @ConfigRoot/@ConfigItem class-based configuration","Rebuild so the generated config-roots service file no longer lists the offending class"],"exampleFix":"// before\n@ConfigRoot(name=\"app\")\npublic class AppConfig { @ConfigItem public String name; }\n// after\n@ConfigRoot(name=\"app\")\n@ConfigMapping(prefix=\"app\")\npublic interface AppConfig { String name(); }","handlingStrategy":"validation","validationCode":"Class<?> c = ...;\nif (!c.isInterface() || c.getAnnotation(ConfigRoot.class) == null || c.getAnnotation(ConfigMapping.class) == null)\n    throw new IllegalStateException(c + \" must be an @ConfigMapping interface annotated with @ConfigRoot\");","typeGuard":"boolean isConfigRootInterface(Class<?> c) { return c.isInterface() && c.isAnnotationPresent(ConfigRoot.class); }","tryCatchPattern":"try { new BuildTimeConfigurationReader(classLoader); } catch (IllegalArgumentException e) { log.error(\"Bad config root registration\", e); throw e; }","preventionTips":["Define all configuration roots as interfaces with @ConfigMapping (plus @ConfigRoot in extensions)","Never register legacy @ConfigRoot class-based configuration","Rebuild extensions after Quarkus upgrades so generated config-roots lists stay current"],"tags":["config","build-time","config-mapping"],"backgroundTag":"config-root-must-be-interface","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"}