{"record":{"id":"8c160cdea5edf645","repo":"halo-dev/halo","slug":"only-zip-extension-supported","errorCode":null,"errorMessage":"Only zip extension supported","messagePattern":"Only zip extension supported","errorType":"http","errorClass":"ServerWebInputException","httpStatus":400,"severity":"error","filePath":"application/src/main/java/run/halo/app/theme/endpoint/ThemeEndpoint.java","lineNumber":453,"sourceCode":"    public record UpgradeFromUriRequest(\n            @Schema(requiredMode = REQUIRED) URI uri) {}\n\n    public static class UpgradeRequest implements IUpgradeRequest {\n\n        private final MultiValueMap<String, Part> multipartData;\n\n        public UpgradeRequest(MultiValueMap<String, Part> multipartData) {\n            this.multipartData = multipartData;\n        }\n\n        @Override\n        public FilePart getFile() {\n            var part = multipartData.getFirst(\"file\");\n            if (!(part instanceof FilePart filePart)) {\n                throw new ServerWebInputException(\"Invalid multipart type of file\");\n            }\n            if (!filePart.filename().endsWith(\".zip\")) {\n                throw new ServerWebInputException(\"Only zip extension supported\");\n            }\n            return filePart;\n        }\n    }\n\n    private Mono<ServerResponse> upgrade(ServerRequest request) {\n        // validate the theme first\n        var name = request.pathVariable(\"name\");\n        return request.multipartData()\n                .map(UpgradeRequest::new)\n                .map(UpgradeRequest::getFile)\n                .flatMap(filePart -> themeService.upgrade(name, filePart.content()))\n                .flatMap((updatedTheme) -> templateEngineManager\n                        .clearCache(updatedTheme.getMetadata().getName())\n                        .thenReturn(updatedTheme))\n                .flatMap(updatedTheme -> ServerResponse.ok().bodyValue(updatedTheme));\n    }\n","sourceCodeStart":435,"sourceCodeEnd":471,"githubUrl":"https://github.com/halo-dev/halo/blob/d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8/application/src/main/java/run/halo/app/theme/endpoint/ThemeEndpoint.java#L435-L471","documentation":"After confirming the 'file' part is a FilePart, UpgradeRequest.getFile() checks that the filename ends with '.zip'. If not, a ServerWebInputException (HTTP 400) 'Only zip extension supported' is thrown. Theme upgrades only accept ZIP archives.","triggerScenarios":"Uploading a non-zip file (e.g. theme.tar.gz, theme.rar, or an uncompressed directory) to the theme upgrade endpoint via multipart. The check is a pure suffix match on filePart.filename().","commonSituations":"User picks the wrong archive format (tar.gz instead of zip); OS hiding extensions causing a misnamed file; a build pipeline producing tar archives.","solutions":["Repackage the theme as a .zip archive and re-upload.","If you produced a .tar.gz, convert it: unzip equivalent or re-zip the theme directory.","Verify the filename actually ends in '.zip' before submitting (case-sensitive suffix match)."],"exampleFix":"# before\n#   zip -r theme.tar.gz theme/   # wrong format\n# after\n#   zip -r theme.zip theme/      # then upload theme.zip","handlingStrategy":"validation","validationCode":"// Client-side: block non-zip filenames before upload\nconst name = file.name.toLowerCase();\nif (!name.endsWith(\".zip\")) {\n    showError(\"Only .zip files are supported.\");\n    return;\n}\n// curl: ensure the uploaded file is a zip\n//   [[ \"$(file -b --mime-type theme.zip)\" == application/zip* ]]","typeGuard":"function isZipFilename(name) {\n  return String(name).toLowerCase().endsWith(\".zip\");\n}","tryCatchPattern":"try { await upgradeTheme(fd); }\ncatch (e) { if (/Only zip extension/.test(e.message)) alert(\"Repackage as .zip first.\"); throw e; }","preventionTips":["Repackage themes as .zip (not .tar.gz/.rar/.7z) before uploading.","Disable the submit button until a .zip is selected.","Watch for double extensions (.zip.txt) which fail the suffix check.","Automate theme packaging with `zip -r theme.zip <dir>/` in CI."],"tags":["theme","multipart","upload","validation"],"backgroundTag":null,"analyzedSha":"d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8","analyzedAt":"2026-08-14T00:18:38.915Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}