quarkusio/quarkus · error · IllegalStateException

Two config roots with different extensions or prefixes canno

Error message

Two config roots with different extensions or prefixes cannot be merged in the same specific config file: ${configRoot.getOverriddenDocFileName()}

What it means

ModelMerger.mergeModel merges config roots that are documented into the same 'specific' config file. Two config roots may share a doc filename only if they have the same extension and prefix; otherwise the merge is ambiguous and throws IllegalStateException naming the overridden doc file name.

Source

Thrown at core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/ModelMerger.java:94

            try (InputStream resolvedModelIs = Files.newInputStream(resolvedModelPath)) {
                ResolvedModel resolvedModel = JacksonMappers.yamlObjectReader().forType(ResolvedModel.class)
                        .readValue(resolvedModelIs);

                if (resolvedModel.getConfigRoots() == null || resolvedModel.getConfigRoots().isEmpty()) {
                    continue;
                }

                for (ConfigRoot configRoot : resolvedModel.getConfigRoots()) {
                    if (configRoot.getOverriddenDocFileName() != null) {
                        ConfigRoot existingConfigRootInSpecificFile = configRootsInSpecificFile
                                .get(configRoot.getOverriddenDocFileName());

                        if (existingConfigRootInSpecificFile == null) {
                            configRootsInSpecificFile.put(configRoot.getOverriddenDocFileName(), configRoot);
                        } else {
                            if (!existingConfigRootInSpecificFile.getExtension().equals(configRoot.getExtension())
                                    || !existingConfigRootInSpecificFile.getPrefix().equals(configRoot.getPrefix())) {
                                throw new IllegalStateException(
                                        "Two config roots with different extensions or prefixes cannot be merged in the same specific config file: "
                                                + configRoot.getOverriddenDocFileName());
                            }

                            existingConfigRootInSpecificFile.merge(configRoot);
                        }

                        continue;
                    }

                    Map<ConfigRootKey, ConfigRoot> extensionConfigRoots = configRoots.computeIfAbsent(
                            normalizeExtension(configRoot.getExtension(), mergeCommonOrInternalExtensions),
                            e -> new TreeMap<>());

                    ConfigRootKey configRootKey = getConfigRootKey(javadocRepository, configRoot);
                    ConfigRoot existingConfigRoot = extensionConfigRoots.get(configRootKey);

                    if (existingConfigRoot == null) {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Give one of the conflicting config roots a distinct doc file name (change its overridden doc file name / @ConfigDocFileName)
  2. Make the extensions/prefixes match if the roots genuinely belong together so they merge instead of conflict
  3. Rename one config root's prefix if it was mistakenly duplicated
  4. Identify the two roots targeting that file and refactor documentation grouping

Example fix

// before
@ConfigMapping(prefix = "ext-a")
@ConfigDoc(fileName = "shared.adoc")
interface AConfig {}
@ConfigMapping(prefix = "ext-b")
@ConfigDoc(fileName = "shared.adoc")
interface BConfig {}
// after
@ConfigMapping(prefix = "ext-b")
@ConfigDoc(fileName = "ext-b.adoc")
interface BConfig {}
Defensive patterns

Strategy: validation

Validate before calling

Map<String, DiscoveredConfigRoot> byFile = new HashMap<>();
for (DiscoveredConfigRoot root : roots) {
    DiscoveredConfigRoot prev = byFile.putIfAbsent(root.getOverriddenDocFileName(), root);
    if (prev != null && (!prev.getExtension().equals(root.getExtension()) || !prev.getPrefix().equals(root.getPrefix()))) {
        throw new IllegalStateException("Conflicting roots share doc file: " + root.getOverriddenDocFileName());
    }
}

Try / catch

try {
    return modelMerger.mergeModel(roots);
} catch (IllegalStateException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Two config roots")) {
        log.error("Assign distinct doc file names: " + e.getMessage());
    }
    throw e;
}

Prevention

When it happens

Trigger: Two @ConfigMapping/@ConfigRoot classes resolve to the same overriddenDocFileName (via @ConfigDocFileName or filename derivation) but declare different extensions or prefixes, so mergeModel encounters a conflict while building the doc model.

Common situations: Two extensions (or an extension and an app) accidentally defining the same config doc file name with different quarkus.* prefixes; copy-pasting a config class and changing the prefix but not the doc file name; re-baselining extension config across modules.

Related errors


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