halo-dev/halo · error · ServerWebInputException
Only zip extension supported
Error message
Only zip extension supported
What it means
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.
Source
Thrown at application/src/main/java/run/halo/app/theme/endpoint/ThemeEndpoint.java:453
public record UpgradeFromUriRequest(
@Schema(requiredMode = REQUIRED) URI uri) {}
public static class UpgradeRequest implements IUpgradeRequest {
private final MultiValueMap<String, Part> multipartData;
public UpgradeRequest(MultiValueMap<String, Part> multipartData) {
this.multipartData = multipartData;
}
@Override
public FilePart getFile() {
var part = multipartData.getFirst("file");
if (!(part instanceof FilePart filePart)) {
throw new ServerWebInputException("Invalid multipart type of file");
}
if (!filePart.filename().endsWith(".zip")) {
throw new ServerWebInputException("Only zip extension supported");
}
return filePart;
}
}
private Mono<ServerResponse> upgrade(ServerRequest request) {
// validate the theme first
var name = request.pathVariable("name");
return request.multipartData()
.map(UpgradeRequest::new)
.map(UpgradeRequest::getFile)
.flatMap(filePart -> themeService.upgrade(name, filePart.content()))
.flatMap((updatedTheme) -> templateEngineManager
.clearCache(updatedTheme.getMetadata().getName())
.thenReturn(updatedTheme))
.flatMap(updatedTheme -> ServerResponse.ok().bodyValue(updatedTheme));
}
View on GitHub (pinned to d2f5165f9c)
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).
Example fix
# before # zip -r theme.tar.gz theme/ # wrong format # after # zip -r theme.zip theme/ # then upload theme.zip
Defensive patterns
Strategy: validation
Validate before calling
// Client-side: block non-zip filenames before upload
const name = file.name.toLowerCase();
if (!name.endsWith(".zip")) {
showError("Only .zip files are supported.");
return;
}
// curl: ensure the uploaded file is a zip
// [[ "$(file -b --mime-type theme.zip)" == application/zip* ]] Type guard
function isZipFilename(name) {
return String(name).toLowerCase().endsWith(".zip");
} Try / catch
try { await upgradeTheme(fd); }
catch (e) { if (/Only zip extension/.test(e.message)) alert("Repackage as .zip first."); throw e; } Prevention
- 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.
When it happens
Trigger: 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().
Common situations: User picks the wrong archive format (tar.gz instead of zip); OS hiding extensions causing a misnamed file; a build pipeline producing tar archives.
Related errors
- Invalid multipart type of file
- Invalid parameter of file, binary data is required
- Invalid file type, only zip format is supported
- Invalid part of file
- Invalid part of policyName
AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14).
Data as JSON: /api/errors/8c160cdea5edf645.
Report an issue: GitHub.