{"record":{"id":"03c816bd426a0ba7","repo":"elastic/elasticsearch","slug":"can-t-create-roles-yml-config-file-from-for","errorCode":null,"errorMessage":"Can't create roles.yml config file from {} for {} as it does not exist","messagePattern":"Can't create roles\\.yml config file from (.+?) for (.+?) as it does not exist","errorType":"exception","errorClass":"TestClustersException","httpStatus":null,"severity":"error","filePath":"build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java","lineNumber":678,"sourceCode":"            logToProcessStdout(\"Setting up \" + credentials.size() + \" users\");\n\n            credentials.forEach(\n                paramMap -> runElasticsearchBinScript(\n                    getVersion().onOrAfter(\"6.3.0\") ? \"elasticsearch-users\" : \"x-pack/users\",\n                    paramMap.entrySet().stream().flatMap(entry -> Stream.of(entry.getKey(), entry.getValue())).toArray(String[]::new)\n                )\n            );\n\n            // If we added users, then also add the standard test roles\n            rolesFile(getBuildPluginFile(\"/roles.yml\"));\n        }\n        if (roleFiles.isEmpty() == false) {\n            logToProcessStdout(\"Setting up roles.yml\");\n\n            Path dst = configFile.getParent().resolve(\"roles.yml\");\n            roleFiles.forEach(from -> {\n                if (Files.exists(from.toPath()) == false) {\n                    throw new TestClustersException(\n                        \"Can't create roles.yml config file from \" + from + \" for \" + this + \" as it does not exist\"\n                    );\n                }\n                try {\n                    final Path source = from.toPath();\n                    final String content = Files.readString(source, StandardCharsets.UTF_8);\n                    Files.writeString(dst, content + System.lineSeparator(), StandardCharsets.UTF_8, StandardOpenOption.APPEND);\n                    LOGGER.info(\"Appended roles file {} to {}\", source, dst);\n                } catch (IOException e) {\n                    throw new UncheckedIOException(\"Can't append roles file \" + from + \" to \" + dst, e);\n                }\n            });\n        }\n    }\n\n    private void installModules() {\n        logToProcessStdout(\"Installing \" + modules.size() + \" modules\");\n        for (Provider<File> module : modules) {","sourceCodeStart":660,"sourceCodeEnd":696,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java#L660-L696","documentation":"Thrown by configureSecurity() during start() when a File added via rolesFile(from) does not exist (Files.exists false). Symmetric to the extra-config-file check, this guards the roles.yml append step so that a missing source file fails loudly instead of silently producing an incomplete roles file.","triggerScenarios":"Calling node.rolesFile(file) with a File that is wrong, generated-too-late, outside the project tree, or missing on the CI checkout. Note configureSecurity() also auto-adds getBuildPluginFile('/roles.yml') when credentials are present; if that bundled resource is unreachable (classpath issue) the same throw fires.","commonSituations":"Custom roles file generated by a task not wired as a dependency. Path relative to wrong subproject. Bundled /roles.yml resource missing from the test classpath after a refactor of build-tools resources.","solutions":["Verify the path in the message exists at start time.","If generated, pass the producing task's output as a Provider so Gradle builds it first.","If the failing source is the bundled /roles.yml, ensure the build-tools jar providing that resource is on the test classpath (not stripped by shading).","Use an absolute or project-relative path resolved via project.layout.projectDirectory."],"exampleFix":"// before: relative file that may not resolve\nrolesFile(new File('roles/custom.yml'))\n// after: project-relative, generated task output wired in\nrolesFile(generateRolesTask.flatMap { it.outputFile.asFile })","handlingStrategy":"validation","validationCode":"node.getRoleFiles().forEach(f -> {\n    if (!Files.exists(f.toPath())) {\n        throw new IllegalStateException(\"rolesFile source missing: \" + f);\n    }\n});\n// If generated, wire Provider<File> from the producing task.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use project-relative paths via project.layout.projectDirectory.","Wire generated roles through task outputs.","Ensure the build-tools /roles.yml bundled resource is on the test classpath."],"tags":["testclusters","security","roles","filesystem","validation"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}