halo-dev/halo · error · ServerWebInputException

Invalid file type, only zip format is supported

Error message

Invalid file type, only zip format is supported

What it means

After confirming the 'file' part is a FilePart, InstallRequest.getFile() resolves the filename to a Path string and checks it ends with '.zip'. If not, a ServerWebInputException (HTTP 400) 'Invalid file type, only zip format is supported' is thrown. Only ZIP archives may be installed.

Source

Thrown at application/src/main/java/run/halo/app/theme/endpoint/ThemeEndpoint.java:531

    @Schema(name = "ThemeInstallRequest", types = "object")
    public static class InstallRequest {

        @Schema(hidden = true)
        private final MultiValueMap<String, Part> multipartData;

        public InstallRequest(MultiValueMap<String, Part> multipartData) {
            this.multipartData = multipartData;
        }

        /** Theme zip file. */
        @Schema(requiredMode = REQUIRED)
        FilePart getFile() {
            Part part = multipartData.getFirst("file");
            if (!(part instanceof FilePart file)) {
                throw new ServerWebInputException("Invalid parameter of file, binary data is required");
            }
            if (!Paths.get(file.filename()).toString().endsWith(".zip")) {
                throw new ServerWebInputException("Invalid file type, only zip format is supported");
            }
            return file;
        }
    }

    /**
     * Payload for installing a theme from a remote URI.
     *
     * @param uri remote URI of the theme ZIP file
     */
    public record InstallFromUriRequest(
            @Schema(requiredMode = REQUIRED) URI uri) {}

    Mono<ServerResponse> install(ServerRequest request) {
        return request.multipartData()
                .map(InstallRequest::new)
                .map(InstallRequest::getFile)
                .flatMap(filePart -> themeService.install(filePart.content()))

View on GitHub (pinned to d2f5165f9c)

Solutions

  1. Repackage the theme as a .zip and re-upload.
  2. Confirm the uploaded filename ends in '.zip' (note the check is on the filename, so double extensions like .zip.txt will fail).
  3. If your theme source is a tarball, convert it to zip before install.

Example fix

# before
#   tar -czf theme.tar.gz theme/
# after
#   zip -r theme.zip theme/
Defensive patterns

Strategy: validation

Validate before calling

// Client-side: enforce .zip before install
const name = Paths.get(file.name) /* conceptual */;
if (!file.name.toLowerCase().endsWith(".zip")) {
    showError("Only zip format is supported.");
    return;
}

Type guard

function isZipArchive(name) {
  return String(name).toLowerCase().endsWith(".zip");
}

Try / catch

try { await installTheme(fd); }
catch (e) { if (/only zip format/.test(e.message)) alert("Use a .zip archive."); throw e; }

Prevention

When it happens

Trigger: Installing a theme packaged as a non-zip archive (.tar.gz, .rar, .7z) or a renamed file whose effective path does not end in .zip. Uses Paths.get(filename).toString().endsWith(".zip") so it normalizes path separators first.

Common situations: Downloading a theme distributed as tar.gz; build tooling emitting the wrong archive format; OS hiding/altering extensions.

Related errors


AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14). Data as JSON: /api/errors/a71ea7e96f6a6902. Report an issue: GitHub.