{"record":{"id":"c94e593cf2c9d1ea","repo":"HMCL-dev/HMCL","slug":"expecting-s-file-in-terracotta-bundle","errorCode":null,"errorMessage":"Expecting %s file in terracotta bundle.","messagePattern":"Expecting (.+?) file in terracotta bundle\\.","errorType":"validation","errorClass":"ArtifactMalformedException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaBundle.java","lineNumber":121,"sourceCode":"                    context.bindProgress(download.progressProperty());\n                    return download.thenSupplyAsync(() -> pkg);\n                });\n    }\n\n    public Task<?> install(Path pkg) {\n        return Task.runAsync(() -> {\n            Files.createDirectories(root);\n            FileUtils.cleanDirectory(root);\n\n            try (TarFileTree tree = TarFileTree.open(pkg)) {\n                for (Map.Entry<String, FileDownloadTask.IntegrityCheck> entry : files.entrySet()) {\n                    String file = entry.getKey();\n                    FileDownloadTask.IntegrityCheck check = entry.getValue();\n\n                    Path path = root.resolve(file);\n                    TarArchiveEntry archive = tree.getEntry(\"/\" + file);\n                    if (archive == null) {\n                        throw new ArtifactMalformedException(String.format(\"Expecting %s file in terracotta bundle.\", file));\n                    }\n\n                    MessageDigest digest = DigestUtils.getDigest(check.algorithm());\n                    try (\n                            InputStream is = tree.getInputStream(archive);\n                            OutputStream os = new DigestOutputStream(Files.newOutputStream(path), digest)\n                    ) {\n                        is.transferTo(os);\n                    }\n\n                    String hash = HexFormat.of().formatHex(digest.digest());\n                    if (!check.checksum().equalsIgnoreCase(hash)) {\n                        throw new ChecksumMismatchException(check.algorithm(), check.checksum(), hash);\n                    }\n\n                    switch (OperatingSystem.CURRENT_OS) {\n                        case LINUX, MACOS, FREEBSD -> Files.setPosixFilePermissions(path, FileUtils.parsePosixFilePermission(archive.getMode()));\n                    }","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaBundle.java#L103-L139","documentation":"TerracottaBundle.install verifies every expected file listed in the bundle manifest exists inside the extracted tar tree before writing it to disk. If an entry is missing from the archive, ArtifactMalformedException is thrown, indicating the downloaded terracotta bundle is incomplete or corrupt.","triggerScenarios":"A downloaded bundle whose tar archive lacks a file referenced by its manifest — truncated download, CDN proxy serving an error page repackaged as tar, or a mismatched manifest/archive pair.","commonSituations":"Flaky network causing partial downloads; intermediary proxies injecting HTML error pages; publishing a bundle with a stale manifest.","solutions":["Re-download the terracotta bundle to replace the corrupt/partial archive","Verify the archive against its published checksum before installing","Ensure the download URL points to a real tar bundle, not an error page or HTML proxy response","Report/refresh the upstream bundle so manifest and archive contents match"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// verify archive contains every manifest entry before install\nfor (String file : manifest.keySet()) { if (tree.getEntry(\"/\" + file) == null) throw new IllegalArgumentException(\"missing entry: \" + file); }","typeGuard":null,"tryCatchPattern":"try { bundle.install(root); } catch (ArtifactMalformedException e) { Files.walk(root).sorted(Comparator.reverseOrder()).forEach(Files::deleteIfExists); /* re-download */ }","preventionTips":["Validate bundle checksum after download and before install","Use resumable, verified downloads on unstable networks","Keep bundle manifest and archive generated by the same build"],"tags":["download","archive","integrity"],"backgroundTag":"checksum-mismatch","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}