{"record":{"id":"3f361f8c5ff932ee","repo":"elastic/elasticsearch","slug":"unable-to-locate-on-build-classpath","errorCode":null,"errorMessage":"Unable to locate {} on build classpath.","messagePattern":"Unable to locate (.+?) on build classpath\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"build-tools/src/main/java/org/elasticsearch/gradle/ReaperService.java","lineNumber":138,"sourceCode":"        }\n    }\n\n    private Path logFilePath() {\n        return getParameters().getInputDir().get().getAsFile().toPath().resolve(\"reaper.log\");\n    }\n\n    private Path locateReaperJar() {\n        if (getParameters().getInternal()) {\n            // when running inside the Elasticsearch build just pull find the jar in the runtime classpath\n            URL main = this.getClass().getClassLoader().getResource(REAPER_CLASS);\n            String mainPath = main.getFile();\n            Matcher matcher = REAPER_JAR_PATH_PATTERN.matcher(mainPath);\n\n            if (matcher.matches()) {\n                String path = matcher.group(1);\n                return Path.of(OS.<String>conditional().onWindows(() -> path.substring(1)).onUnix(() -> path).supply());\n            } else {\n                throw new RuntimeException(\"Unable to locate \" + REAPER_CLASS + \" on build classpath.\");\n            }\n        } else {\n            // copy the reaper jar\n            Path jarPath = getParameters().getBuildDir().get().getAsFile().toPath().resolve(\"reaper\").resolve(\"reaper.jar\");\n            try {\n                Files.createDirectories(jarPath.getParent());\n            } catch (IOException e) {\n                throw new UncheckedIOException(\"Unable to create reaper JAR output directory \" + jarPath.getParent(), e);\n            }\n\n            try (\n                OutputStream out = Files.newOutputStream(jarPath);\n                InputStream jarInput = this.getClass().getResourceAsStream(\"/META-INF/reaper.jar\");\n            ) {\n                logger.info(\"Copying reaper.jar...\");\n                jarInput.transferTo(out);\n            } catch (IOException e) {\n                throw new UncheckedIOException(e);","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools/src/main/java/org/elasticsearch/gradle/ReaperService.java#L120-L156","documentation":"ReaperService.locateReaperJar() throws RuntimeException (internal mode) when the URL of the reaper class resource does not match the REAPER_JAR_PATH_PATTERN regex `file:(.*)!/org/elasticsearch/gradle/reaper/Reaper.class`. In internal mode the build expects the class to live inside a JAR on the runtime classpath (a `file:...!/...` jar URL); a mismatch means the class is loaded from an exploded directory, a different protocol, or the JAR isn't where the build expects it.","triggerScenarios":"Running build-tools in internal mode (Params.internal = true) where getClass().getClassLoader().getResource(REAPER_CLASS) returns a URL that is not a JAR-entry URL — e.g., the class is on an exploded classpath (IDE run configs, some test harnesses), the resource is missing (main == null → NPE on main.getFile() would actually fire first), or the URL uses a non-file protocol.","commonSituations":"Running build-tools from an IDE with exploded class output instead of the packaged JAR; a broken build-tools JAR that omits the reaper classes; a custom classloader that returns a non-standard resource URL scheme.","solutions":["Ensure build-tools is packaged as a JAR on the runtime classpath (run the normal Gradle build, not an IDE exploded run).","If main == null (resource missing), the build-tools JAR is incomplete — rebuild it with the reaper classes included.","For non-internal contexts, use Params.internal = false so the JAR is copied from /META-INF/reaper.jar instead of located on the classpath."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"URL main = ReaperService.class.getClassLoader().getResource(\"org/elasticsearch/gradle/reaper/Reaper.class\");\nif (main == null || !main.getFile().contains(\"!/.\")) {\n    throw new IllegalStateException(\"Reaper class not packaged in a JAR; rebuild build-tools\");\n}","typeGuard":null,"tryCatchPattern":"try { Path jar = reaperService.locateReaperJar(); } catch (RuntimeException e) { /* ensure build-tools JAR contains reaper classes */ throw e; }","preventionTips":["Run the normal Gradle build so build-tools is packaged as a JAR.","Avoid IDE exploded-classpath runs that bypass JAR packaging.","Use Params.internal = false to copy the JAR from /META-INF/reaper.jar when classpath layout is uncertain."],"tags":["gradle","build-tools","reaper","classpath","packaging"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}