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
- Give one of the conflicting config roots a distinct doc file name (change its overridden doc file name / @ConfigDocFileName)
- Make the extensions/prefixes match if the roots genuinely belong together so they merge instead of conflict
- Rename one config root's prefix if it was mistakenly duplicated
- 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
- Ensure each config root has a unique doc file name unless extensions and prefixes match
- Don't copy config classes without updating prefix and doc file name
- Run doc generation across all extension modules in CI to catch conflicts early
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
- Unable to find javadoc for config item ${enclosingElement} $
- Starting with Quarkus 3.25, legacy config classes (deprecate
- Unable to read the resolved model from: ${resolvedModelPath}
- Unable to get element as unwrappedType is not a DeclaredType
- Conversion from Markdown to Asciidoc is not supported
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/6ef4e5586a4bbff0.
Report an issue: GitHub.