{"record":{"id":"ffc2d6cf4386de8b","repo":"quarkusio/quarkus","slug":"unable-to-render-config-roots-for-top-level-prefix","errorCode":null,"errorMessage":"Unable to render config roots for top level prefix: ${topLevelPrefix} in extension: ${extension}","messagePattern":"Unable to render config roots for top level prefix: (.+?) in extension: (.+?)","errorType":"exception","errorClass":"MojoExecutionException","httpStatus":null,"severity":"error","filePath":"devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateConfigDocMojo.java","lineNumber":134,"sourceCode":"            Path configRootPath = null;\n\n            for (Entry<ConfigRootKey, ConfigRoot> configRootEntry : extensionConfigRootsEntry.getValue().entrySet()) {\n                String topLevelPrefix = configRootEntry.getKey().topLevelPrefix();\n                ConfigRoot configRoot = configRootEntry.getValue();\n\n                // here we generate a file even if there are no items as it's used for the Reactive Oracle SQL client\n\n                configRootPath = resolvedTargetDirectory.resolve(String.format(CONFIG_ROOT_FILE_FORMAT,\n                        extension.artifactId(), topLevelPrefix, normalizedFormat.getExtension()));\n                String summaryTableId = formatter\n                        .toAnchor(extension.artifactId() + \"_\" + topLevelPrefix);\n                Context context = new Context(summaryTableId, false);\n\n                try {\n                    Files.writeString(configRootPath,\n                            generateConfigReference(quteEngine, context, extension, configRoot, \"\", true));\n                } catch (Exception e) {\n                    throw new MojoExecutionException(\"Unable to render config roots for top level prefix: \" + topLevelPrefix\n                            + \" in extension: \" + extension, e);\n                }\n            }\n\n            // if we have only one top level prefix, we copy the generated file to a file named after the extension\n            // for simplicity's sake\n            if (extensionConfigRootsEntry.getValue().size() == 1 && configRootPath != null) {\n                Path extensionPath = resolvedTargetDirectory.resolve(String.format(EXTENSION_FILE_FORMAT,\n                        extension.artifactId(), normalizedFormat.getExtension()));\n\n                try {\n                    Files.copy(configRootPath, extensionPath, StandardCopyOption.REPLACE_EXISTING);\n                } catch (Exception e) {\n                    throw new MojoExecutionException(\"Unable to copy extension file for: \" + extension, e);\n                }\n            }\n        }\n","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateConfigDocMojo.java#L116-L152","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\n<outputDirectory>/docs/readonly</outputDirectory>\n// after\n<outputDirectory>${project.build.directory}/generated-docs</outputDirectory>\n// or fix the cause reported in the nested exception","handlingStrategy":"try-catch","validationCode":"// pre-check output location\nPath out = resolvedTargetDirectory;\nif (!Files.isDirectory(out)) Files.createDirectories(out);\nif (!Files.isWritable(out)) throw new IllegalStateException(\"Not writable: \" + out);","typeGuard":null,"tryCatchPattern":"try {\n    String rendered = generateConfigReference(quteEngine, context, extension, configRoot, \"\", true);\n    Files.writeString(configRootPath, rendered);\n} catch (Exception e) {\n    throw new MojoExecutionException(\"Unable to render config roots for prefix \" + topLevelPrefix, e);\n}","preventionTips":["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."],"tags":["maven-plugin","qute","config-docs","rendering"],"backgroundTag":"template-render-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"}