{"record":{"id":"6cd3d09f4d414c1e","repo":"elastic/elasticsearch","slug":"expected-noticepath-to-contain-expectedline","errorCode":null,"errorMessage":"expected [${noticePath} to contain [${expectedLine}] but it did not","messagePattern":"expected \\[(.+?) to contain \\[(.+?)\\] but it did not","errorType":"validation","errorClass":"GradleException","httpStatus":null,"severity":"error","filePath":"build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/InternalDistributionArchiveCheckPlugin.java","lineNumber":125,"sourceCode":"            task.dependsOn(checkExtraction);\n            final Provider<Path> noticePath = checkExtraction.map(\n                c -> c.getDestinationDir()\n                    .toPath()\n                    .resolve(\"elasticsearch-\" + VersionProperties.getElasticsearch() + \"/modules/x-pack-ml/NOTICE.txt\")\n            );\n            ListProperty<String> expectedMlLicenses = extension.expectedMlLicenses;\n            task.doLast(new Action<Task>() {\n                @Override\n                public void execute(Task task) {\n                    // this is just a small sample from the C++ notices,\n                    // the idea being that if we've added these lines we've probably added all the required lines\n                    final List<String> expectedLines = expectedMlLicenses.get();\n                    final List<String> actualLines;\n                    try {\n                        actualLines = Files.readAllLines(noticePath.get());\n                        for (final String expectedLine : expectedLines) {\n                            if (actualLines.contains(expectedLine) == false) {\n                                throw new GradleException(\n                                    \"expected [\" + noticePath.get() + \" to contain [\" + expectedLine + \"] but it did not\"\n                                );\n                            }\n                        }\n                    } catch (IOException ioException) {\n                        ioException.printStackTrace();\n                    }\n                }\n            });\n        });\n        return checkMlCppNoticeTask;\n    }\n\n    private TaskProvider<Task> registerCheckNoticeTask(Project project, TaskProvider<Copy> checkExtraction) {\n        return project.getTasks().register(\"checkNotice\", task -> {\n            task.dependsOn(checkExtraction);\n            var noticePath = checkExtraction.map(\n                copy -> copy.getDestinationDir().toPath().resolve(\"elasticsearch-\" + VersionProperties.getElasticsearch() + \"/NOTICE.txt\")","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/InternalDistributionArchiveCheckPlugin.java#L107-L143","documentation":"Thrown as GradleException inside the checkMlCppNotice task's doLast action when an expected license line from expectedMlLicenses is not present in the extracted x-pack-ml/NOTICE.txt. The check uses a sample of lines from the C++ notices as a proxy for full compliance — if those sample lines are missing, the notice is considered non-compliant.","triggerScenarios":"The task (registered only for projects whose name contains 'zip' or 'tar', excluding integ-test-zip) reads NOTICE.txt from the extracted archive and checks each entry in extension.expectedMlLicenses via actualLines.contains(). A missing expected line throws at line 125.","commonSituations":"A new ML C++ dependency was added but its license line wasn't appended to NOTICE.txt generation; the notice file template changed; a dependency upgrade changed the upstream license text; the expectedMlLicenses extension was populated with outdated line content.","solutions":["Diff the generated NOTICE.txt against the expected lines; add the missing license text to the ML notice generation source.","Update distributionArchiveCheckExtension.expectedMlLicenses if the expected line content legitimately changed (dependency upgrade).","Rebuild the distribution to regenerate NOTICE.txt before running checkMlCppNotice.","Verify the x-pack-ml module is actually packaged and its NOTICE.txt is at the expected path."],"exampleFix":"// before: expected line missing from NOTICE.txt\nextension.expectedMlLicenses.add(\"Some Library 1.0 — BSD 2-Clause\");\n// NOTICE.txt lacks this exact line → throws\n\n// after: add the line to the ML notice template/generator,\n// or correct the expected string to match what ships\nextension.expectedMlLicenses.add(\"Some Library 1.0 - BSD 2-Clause\"); // match exact text","handlingStrategy":"try-catch","validationCode":"List<String> actual = Files.readAllLines(noticePath);\nfor (String expected : expectedMlLicenses) {\n    if (actual.contains(expected) == false) {\n        System.err.println(\"Missing ML notice line: \" + expected);\n    }\n}","typeGuard":null,"tryCatchPattern":"task.doLast(t -> {\n    try {\n        // check lines\n    } catch (Exception e) {\n        throw new GradleException(\"checkMlCppNotice failed: \" + e.getMessage(), e);\n    }\n});","preventionTips":["Keep expectedMlLicenses in sync with ML dependency changes.","Regenerate NOTICE.txt after adding C++ ML dependencies.","Run checkMlCppNotice locally before pushing distribution changes.","Review NOTICE.txt diffs in the ML module when upgrading dependencies."],"tags":["distribution","licensing","ml","notice","compliance"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:17:08.281Z"}