quarkusio/quarkus · error · MojoExecutionException
Unable to render config roots for top level prefix: ${topLev
Error message
Unable to render config roots for top level prefix: ${topLevelPrefix} in extension: ${extension} What it means
The config-doc-maven-plugin's GenerateConfigDocMojo renders per-config-root Asciidoc reference pages using Qute. When generateConfigReference(...) or Files.writeString fails for a top-level prefix's config root, the exception is wrapped in this MojoExecutionException naming the prefix and extension. It indicates Qute template rendering or file output failed for that config root.
Source
Thrown at devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateConfigDocMojo.java:134
Path configRootPath = null;
for (Entry<ConfigRootKey, ConfigRoot> configRootEntry : extensionConfigRootsEntry.getValue().entrySet()) {
String topLevelPrefix = configRootEntry.getKey().topLevelPrefix();
ConfigRoot configRoot = configRootEntry.getValue();
// here we generate a file even if there are no items as it's used for the Reactive Oracle SQL client
configRootPath = resolvedTargetDirectory.resolve(String.format(CONFIG_ROOT_FILE_FORMAT,
extension.artifactId(), topLevelPrefix, normalizedFormat.getExtension()));
String summaryTableId = formatter
.toAnchor(extension.artifactId() + "_" + topLevelPrefix);
Context context = new Context(summaryTableId, false);
try {
Files.writeString(configRootPath,
generateConfigReference(quteEngine, context, extension, configRoot, "", true));
} catch (Exception e) {
throw new MojoExecutionException("Unable to render config roots for top level prefix: " + topLevelPrefix
+ " in extension: " + extension, e);
}
}
// if we have only one top level prefix, we copy the generated file to a file named after the extension
// for simplicity's sake
if (extensionConfigRootsEntry.getValue().size() == 1 && configRootPath != null) {
Path extensionPath = resolvedTargetDirectory.resolve(String.format(EXTENSION_FILE_FORMAT,
extension.artifactId(), normalizedFormat.getExtension()));
try {
Files.copy(configRootPath, extensionPath, StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
throw new MojoExecutionException("Unable to copy extension file for: " + extension, e);
}
}
}
View on GitHub (pinned to e1c734241f)
Solutions
- Read the wrapped cause in the MojoExecutionException — it names the actual rendering (Qute) or IO failure; fix that first.
- Run with -e / -X (mvn -e) to see the full stack trace and identify the failing template or path.
- Verify the output directory (resolvedTargetDirectory) exists and is writable.
- Rebuild after fixing the extension's @ConfigMapping annotations if the config model itself is malformed.
Example fix
// before
<outputDirectory>/docs/readonly</outputDirectory>
// after
<outputDirectory>${project.build.directory}/generated-docs</outputDirectory>
// or fix the cause reported in the nested exception Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check output location
Path out = resolvedTargetDirectory;
if (!Files.isDirectory(out)) Files.createDirectories(out);
if (!Files.isWritable(out)) throw new IllegalStateException("Not writable: " + out); Try / catch
try {
String rendered = generateConfigReference(quteEngine, context, extension, configRoot, "", true);
Files.writeString(configRootPath, rendered);
} catch (Exception e) {
throw new MojoExecutionException("Unable to render config roots for prefix " + topLevelPrefix, e);
} Prevention
- Run with mvn -e to surface the nested Qute/IO cause immediately.
- Keep plugin and extension versions aligned so templates match the config model.
- Ensure the docs output directory exists and is writable before the goal runs.
When it happens
Trigger: Running the generate-config-doc goal when rendering a config root of a top level prefix throws — e.g. a Qute template error, invalid template data, or inability to write configRootPath (missing parent dir, permissions).
Common situations: Generating docs for an extension whose config mapping produces data the Qute templates can't handle (new/changed config root shape); read-only target directory; malformed extension metadata in the merged model.
Related errors
- Unable to render config roots for specific file: ${fileName}
- Unable to copy extension file for: ${extension}
- Unable to parse: ${resolvedModelPath}
- Unknown item type: ${otherItem.getClass()}
- Unknown item type: ${otherItem.getClass()}
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/ffc2d6cf4386de8b.
Report an issue: GitHub.