{"record":{"id":"1f34416bb997d10b","repo":"quarkusio/quarkus","slug":"unable-to-scan-config-mapping-without-config-root","errorCode":null,"errorMessage":"Unable to scan config mapping without config root: ${configMappingWithoutConfigRoot}","messagePattern":"Unable to scan config mapping without config root: (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigAnnotationScanner.java","lineNumber":151,"sourceCode":"                continue;\n            }\n\n            if (isConfigMappingWithoutConfigRootAlreadyHandled(configMappingWithoutConfigRoot)) {\n                continue;\n            }\n\n            debug(\"Detected config mapping without config root: \" + configMappingWithoutConfigRoot,\n                    configMappingWithoutConfigRoot);\n\n            try {\n                // we need to forge a dummy DiscoveryConfigRoot, it's mostly ignored in the listeners\n                DiscoveryConfigRoot discoveryConfigRoot = new DiscoveryConfigRoot(config.getExtension(), \"dummy\", \"dummy\",\n                        utils.element().getBinaryName(configMappingWithoutConfigRoot),\n                        configMappingWithoutConfigRoot.getQualifiedName().toString(),\n                        ConfigPhase.BUILD_TIME, null);\n                scanElement(configMappingWithoutConfigRootListeners, discoveryConfigRoot, configMappingWithoutConfigRoot);\n            } catch (Exception e) {\n                throw new IllegalStateException(\n                        \"Unable to scan config mapping without config root: \" + configMappingWithoutConfigRoot, e);\n            }\n        }\n    }\n\n    public ConfigCollector finalizeProcessing() {\n        applyListeners(configRootListeners, l -> l.finalizeProcessing());\n        applyListeners(configMappingWithoutConfigRootListeners, l -> l.finalizeProcessing());\n\n        return configCollector;\n    }\n\n    private void scanElement(List<ConfigAnnotationListener> listeners, DiscoveryRootElement configRootElement,\n            TypeElement clazz) {\n        // we scan the superclass and interfaces first so that the local elements can potentially override them\n        if (clazz.getKind() == ElementKind.INTERFACE) {\n            List<? extends TypeMirror> superInterfaces = clazz.getInterfaces();\n            for (TypeMirror superInterface : superInterfaces) {","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigAnnotationScanner.java#L133-L169","documentation":"The processor failed while scanning @ConfigMapping classes that have no corresponding @ConfigRoot. To document them, it fabricates a dummy DiscoveryConfigRoot and scans the mapping; any exception in that scan is wrapped in this IllegalStateException naming the mapping interface.","triggerScenarios":"Compiling an application with a @ConfigMapping interface (no @ConfigRoot) whose members trip the scanner: unsupported property types, unregistered enums, recursive/nested groups that cannot be resolved, or Javadoc listeners failing on that element.","commonSituations":"Application developers using @ConfigMapping for app config; a mapping with an exotic type like Optional<List<SomeUnregisteredClass>>; mappings referencing generated sources that are not on the annotation-processing path; Quarkus upgrade changing scan behavior.","solutions":["Read the 'Caused by' chain to find the real failure inside the mapping scan.","Check every property type in the named interface is a supported config type and that referenced enums are simple/registered.","Ensure any referenced nested groups are visible/resolvable at processing time (avoid referencing classes not yet compiled).","Clean rebuild to eliminate stale generated sources.","If a supported pattern fails, reduce the interface and file a Quarkus issue with the reproducer."],"exampleFix":"// before: type the scanner cannot document\n@ConfigMapping(prefix = \"app\")\npublic interface AppConfig {\n    Optional<List<MyOpaqueClass>> items(); // fails\n}\n\n// after\n@ConfigMapping(prefix = \"app\")\npublic interface AppConfig {\n    Optional<List<Integer>> items();\n}","handlingStrategy":"validation","validationCode":"// scan mapping interfaces for unsupported shapes before compiling\nfor (var m : AppConfig.class.getDeclaredMethods()) {\n    Class<?> t = m.getReturnType();\n    if (t == Optional.class && !isSupportedConfigType(genericArg(m)))\n        throw new IllegalStateException(\"Unsupported mapping type: \" + m);\n}","typeGuard":"static boolean isDocumentableMapping(Class<?> c) {\n    return c.isAnnotationPresent(ConfigMapping.class)\n        && Arrays.stream(c.getDeclaredMethods())\n            .allMatch(m -> isSupportedConfigType(m.getReturnType()));\n}","tryCatchPattern":null,"preventionTips":["Keep @ConfigMapping property types simple and supported.","Avoid referencing not-yet-generated classes from mappings.","Reference enums directly as property types so they get registered.","Rebuild cleanly after Quarkus upgrades."],"tags":["quarkus","annotation-processing","config-mapping","build","scanner"],"backgroundTag":"annotation-scanning-failed","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"}