halo-dev/halo · error · ServerWebInputException

Invalid parameter of file, binary data is required

Error message

Invalid parameter of file, binary data is required

What it means

InstallRequest.getFile() reads the multipart part named 'file' from the install request. If that part exists but is not a FilePart (no filename / not binary), a ServerWebInputException (HTTP 400) 'Invalid parameter of file, binary data is required' is thrown. This is the install counterpart of the upgrade file-type guard, checked before the .zip extension.

Source

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

    }

    /** Multipart payload for installing a theme. */
    @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()

View on GitHub (pinned to d2f5165f9c)

Solutions

  1. Send a real binary file part: curl -F 'file=@theme.zip'.
  2. On the frontend, append a File object: formData.append('file', fileInput.files[0]).
  3. Ensure the part's Content-Disposition includes a filename.
  4. Do not manually set Content-Type: multipart/form-data; let the browser set the boundary.

Example fix

// before (frontend, wrong): sending a string
//   formData.append('file', 'some-value')
// after: append the actual File
//   formData.append('file', fileInput.files[0], 'theme.zip')
Defensive patterns

Strategy: validation

Validate before calling

// Client-side: ensure binary File is attached for install
const f = fileInput.files && fileInput.files[0];
if (!(f instanceof File)) {
    showError("Binary file data is required.");
    return;
}
const fd = new FormData();
fd.append("file", f);

Type guard

function isBinaryFilePart(v) { return v instanceof File; }

Try / catch

try { await installTheme(fd); }
catch (e) { if (/binary data is required/.test(e.message)) alert("Attach a file, not text."); throw e; }

Prevention

When it happens

Trigger: POST to the theme install endpoint with a multipart 'file' part sent as a plain text form field rather than a binary file part. Reached via the install handler -> InstallRequest::getFile.

Common situations: Wrong curl/Postman usage (-F file=value instead of -F file=@path); frontend appending a non-File value to FormData; malformed Content-Disposition missing the filename parameter.

Related errors


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