{"record":{"id":"4a440bfa1f256c24","repo":"elastic/elasticsearch","slug":"two-different-notices-exist-for-dependency-name","errorCode":null,"errorMessage":"Two different notices exist for dependency '${name}': ${prevFile} and ${file}","messagePattern":"Two different notices exist for dependency '(.+?)': (.+?) and (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/NoticeTask.java","lineNumber":116,"sourceCode":"    }\n\n    @TaskAction\n    public void generateNotice() throws IOException {\n        StringBuilder output = new StringBuilder();\n        output.append(readFileToString(inputFile, \"UTF-8\"));\n        output.append(\"\\n\\n\");\n        // This is a map rather than a set so that the sort order is the 3rd\n        // party component names, unaffected by the full path to the various files\n        final Map<String, File> seen = new TreeMap<String, File>();\n        FileCollection noticeFiles = getNoticeFiles();\n        if (noticeFiles != null) {\n            for (File file : getNoticeFiles()) {\n                String name = file.getName().replaceFirst(\"-NOTICE\\\\.txt$\", \"\");\n                if (seen.containsKey(name)) {\n                    File prevFile = seen.get(name);\n                    String previousFileText = readFileToString(prevFile, \"UTF-8\");\n                    if (previousFileText.equals(readFileToString(file, \"UTF-8\")) == false) {\n                        throw new RuntimeException(\n                            \"Two different notices exist for dependency '\" + name + \"': \" + prevFile + \" and \" + file\n                        );\n                    }\n                } else {\n                    seen.put(name, file);\n                }\n            }\n        }\n\n        // Add all LICENSE and NOTICE files in licenses directory\n        seen.forEach((name, file) -> {\n            appendFile(file, name, \"NOTICE\", output);\n            appendFile(new File(file.getParentFile(), name + \"-LICENSE.txt\"), name, \"LICENSE\", output);\n        });\n\n        // Find any source files with \"@notice\" annotated license header\n        for (File sourceFile : sources.getFiles()) {\n            boolean isPackageInfo = sourceFile.getName().equals(\"package-info.java\");","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/NoticeTask.java#L98-L134","documentation":"Thrown by NoticeTask.generateNotice when two NOTICE files for the same dependency name (the filename with the '-NOTICE.txt' suffix stripped) are found in the configured license directories and their textual contents differ. The task de-duplicates by dependency name so each component appears once in the aggregated NOTICE; conflicting notices for one name indicate a licensing inconsistency that must be resolved manually.","triggerScenarios":"getNoticeFiles() collects '**/*-NOTICE.txt' across getLicenseDirs() (default: ${projectDir}/licenses plus any added via licensesDir). For each file, the name key is fileName.replaceFirst(\"-NOTICE\\\\.txt$\", \"\"). If the key was seen before, both files are read as UTF-8 and compared; a byte-level difference throws.","commonSituations":"A dependency upgrade ships a new NOTICE whose text differs, but the old '-NOTICE.txt' was not removed/updated; two dependencies sharing a base name (e.g. 'jackson-core' vs 'jackson-core-annotations') collide after stripping; merge conflicts leaving two NOTICE files for the same component; line-ending or trailing-whitespace differences (the comparison is exact, not normalized).","solutions":["Identify both file paths in the message; open them and reconcile the content — keep a single canonical NOTICE for that dependency.","If the old file is stale (dependency upgraded), delete the obsolete '-NOTICE.txt' so only the current one remains.","If the two files are meant for genuinely different artifacts, rename one so the stripped name differs (e.g. 'jackson-databind-NOTICE.txt' vs 'jackson-core-NOTICE.txt').","Ensure identical line endings and no trailing-whitespace drift if the notices are supposed to be equal."],"exampleFix":"// before: licenses/jackson-core-NOTICE.txt and licenses/jackson-core-NOTICE.txt.bak differ\n// after: delete the stale .bak / duplicate file so only one jackson-core-NOTICE.txt remains","handlingStrategy":"validation","validationCode":"// Before the build, assert no dependency has two divergent NOTICE files:\nimport java.nio.file.*;\nimport java.util.*;\nMap<String, Path> seen = new TreeMap<>();\ntry (var s = Files.list(Path.of(\"licenses\"))) {\n    s.filter(p -> p.getFileName().toString().endsWith(\"-NOTICE.txt\")).forEach(p -> {\n        String key = p.getFileName().toString().replaceFirst(\"-NOTICE\\\\.txt$\", \"\");\n        if (seen.containsKey(key)) {\n            String a = Files.readString(seen.get(key));\n            String b = Files.readString(p);\n            if (!a.equals(b)) throw new IllegalStateException(\n                \"conflict for \" + key + \": \" + seen.get(key) + \" vs \" + p);\n        } else seen.put(key, p);\n    });\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When upgrading a dependency, replace its old '-NOTICE.txt' rather than adding a second one.","Give each NOTICE file a unique dependency-name key (the part before '-NOTICE.txt').","Normalize line endings before committing NOTICE files; the comparison is exact."],"tags":["gradle","licensing","notice","dependencies","compliance"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}