{"record":{"id":"66be363db00c5bcc","repo":"elastic/elasticsearch","slug":"error-patching-s-the-jar-does-not-contain-s","errorCode":null,"errorMessage":"error patching [%s]: the jar does not contain [%s]","messagePattern":"error patching \\[(.+?)\\]: the jar does not contain \\[(.+?)\\]","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/dependencies/patches/Utils.java","lineNumber":153,"sourceCode":"            throw new RuntimeException(ex);\n        }\n\n        if (mismatchedClasses.isEmpty() == false) {\n            throw new IllegalArgumentException(\n                String.format(\n                    Locale.ROOT,\n                    \"\"\"\n                        Error patching JAR [%s]: SHA256 digest mismatch (%s). This JAR was updated to a version that contains different \\\n                        classes, for which this patcher was not designed. Please check if the patcher still \\\n                        applies correctly, and update the SHA256 digest(s).\"\"\",\n                    inputFile.getName(),\n                    mismatchedClasses.stream().map(MismatchInfo::toString).collect(Collectors.joining())\n                )\n            );\n        }\n\n        if (classPatchers.isEmpty() == false) {\n            throw new IllegalArgumentException(\n                String.format(\n                    Locale.ROOT,\n                    \"error patching [%s]: the jar does not contain [%s]\",\n                    inputFile.getName(),\n                    String.join(\", \", classPatchers.keySet())\n                )\n            );\n        }\n    }\n}\n","sourceCodeStart":135,"sourceCodeEnd":164,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/dependencies/patches/Utils.java#L135-L164","documentation":"Thrown by Utils.patchJar after iterating all JAR entries: the classPatchers map (built from the requested PatcherInfo collection) still has leftover entries, meaning the input JAR did not contain class entries matching the patcher jar-entry names. The patcher targets specific class paths inside the JAR (e.g. 'com/foo/Bar.class') and cannot proceed if those entries are absent, because patching nothing would silently produce an unpatched JAR.","triggerScenarios":"A dependency JAR is upgraded to a version where the target class was renamed, moved to a different package, or removed entirely; or a PatcherInfo was configured with a typo'd jarEntryName that does not correspond to any entry in the JAR being patched.","commonSituations":"Bumping a third-party dependency version (e.g. jackson, lucene) in build-conventions or a module's build.gradle without updating the corresponding patch definitions; the upstream library refactored or deleted the patched class. Less commonly, a patcher was authored against a wrong or typo'd entry name from the start.","solutions":["Open the input JAR with 'jar tf <file>.jar | grep <className>' and find the actual entry name for the class you intend to patch.","Update the PatcherInfo jarEntryName in the patch configuration (e.g. the JarRelocation/Patches block in build-conventions or the module's build.gradle) to match the real entry path.","If the class was genuinely removed in the new dependency version, remove the now-obsolete PatcherInfo entry and verify the patch is no longer needed.","Pin the dependency to the version the patcher was written for until the patcher is updated."],"exampleFix":"// before: patcher references an entry that no longer exists\nnew PatcherInfo(\"com/fasterxml/jackson/databind/ObjectMapper.class\", sha, visitor)\n\n// after: confirm actual entry name in the upgraded JAR and update\n// run: jar tf jackson-databind-2.17.0.jar | grep ObjectMapper\nnew PatcherInfo(\"com/fasterxml/jackson/databind/ObjectMapper.class\", newSha, visitor)","handlingStrategy":"validation","validationCode":"// Before calling Utils.patchJar, confirm every patcher entry exists in the JAR\nimport java.util.jar.JarFile;\nimport java.util.HashSet;\nimport java.util.Set;\n\nvoid verifyPatchTargets(File inputJar, Collection<PatcherInfo> patchers) throws IOException {\n    Set<String> entries;\n    try (JarFile jf = new JarFile(inputJar)) {\n        entries = new HashSet<>(jf.stream().map(JarEntry::getName).toList());\n    }\n    var missing = patchers.stream()\n        .map(PatcherInfo::jarEntryName)\n        .filter(e -> !entries.contains(e))\n        .toList();\n    if (!missing.isEmpty()) {\n        throw new IllegalStateException(\n            \"JAR \" + inputJar + \" is missing patch targets: \" + missing\n            + \". Available entries containing the class name: \"\n            + entries.stream().filter(e -> e.contains(missing.get(0).replace('/', '.').replace(\".class\",\"\"))).toList()\n        );\n    }\n}\n// call: verifyPatchTargets(inputFile, patchers);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When upgrading a patched dependency, run 'jar tf <new>.jar | grep <ClassName>' and diff against the patcher's jarEntryName before building.","Keep patcher configurations (PatcherInfo entries) in a single discoverable location (e.g. build-conventions) so upgrades surface all affected patchers at once.","Add a CI step that verifies all patch target entry names exist in the resolved JAR before the patch task runs."],"tags":["build","dependencies","jar","patching","asm","gradle"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T06:17:24.410Z"}