halo-dev/halo · error · ServerWebInputException
Invalid file type, only jar is supported
Error message
Invalid file type, only jar is supported
What it means
Thrown as a ServerWebInputException (HTTP 400) by the plugin install endpoint's InstallRequest.getFile() when the uploaded multipart part named 'file' exists and is a FilePart but its filename does not end with '.jar'. Halo only accepts Java plugin archives, so any other extension (zip, tar, no extension) is rejected before the install pipeline runs.
Source
Thrown at application/src/main/java/run/halo/app/core/endpoint/console/PluginEndpoint.java:662
private final MultiValueMap<String, Part> multipartData;
public InstallRequest(MultiValueMap<String, Part> multipartData) {
this.multipartData = multipartData;
}
/** Plugin JAR file. */
@Schema(requiredMode = NOT_REQUIRED)
public FilePart getFile() {
var part = multipartData.getFirst("file");
if (part == null) {
throw new ServerWebInputException("Form field file is required");
}
if (!(part instanceof FilePart file)) {
throw new ServerWebInputException("Invalid parameter of file");
}
if (!Paths.get(file.filename()).toString().endsWith(".jar")) {
throw new ServerWebInputException("Invalid file type, only jar is supported");
}
return file;
}
/** Plugin preset name. Halo finds the plugin from plugin presets. */
@Schema(requiredMode = NOT_REQUIRED)
public Mono<String> getPresetName() {
var part = multipartData.getFirst("presetName");
if (part == null) {
return Mono.error(new ServerWebInputException("Form field presetName is required."));
}
if (!(part instanceof FormFieldPart presetName)) {
return Mono.error(new ServerWebInputException("Invalid format of presetName field, string required"));
}
if (!StringUtils.hasText(presetName.value())) {
return Mono.error(new ServerWebInputException("presetName must not be blank"));
}
return Mono.just(presetName.value());View on GitHub (pinned to d2f5165f9c)
Solutions
- Re-package the plugin as a .jar (run './gradlew build' and grab the jar from build/libs), then upload the artifact whose filename literally ends in lowercase '.jar'.
- Check the exact filename your client sends (including hidden extensions and case) and rename it so it ends with '.jar'.
- If installing from a preset or URL instead, set the 'source' form field to PRESET or URL and provide presetName/url instead of the file part.
Example fix
// before: client sends plugin.zip as the 'file' part // after: build and send the jar // ./gradlew :build // upload build/libs/my-plugin-1.0.0.jar with field name 'file'
Defensive patterns
Strategy: validation
Validate before calling
// before building the install request, check the file's name
String name = selectedFile.getName();
if (!name.toLowerCase(Locale.ROOT).endsWith(".jar")) {
throw new IllegalArgumentException("Select a .jar plugin archive, got: " + name);
} Prevention
- Gate the upload UI to accept only .jar files via the file input's accept='.jar' attribute.
- Run the plugin build ('./gradlew build') and upload the artifact from build/libs, never a source zip.
When it happens
Trigger: POST /apis/api.console.halo.run/v1alpha1/plugins/install with a multipart/form-data body whose 'file' field is a non-.jar file (e.g. plugin.zip, plugin.tar.gz, screenshot.png, or a jar with trailing case differences like .JAR on a case-sensitive path check). Paths.get(file.filename()).toString().endsWith(".jar") is case-sensitive, so '.JAR' or '.Jar' will also fail.
Common situations: User zips the plugin source instead of building the jar; plugin author distributes a .zip that must be unpacked; Windows user with hidden extensions uploads 'plugin.jar.zip'; case mismatch from a renamed file; plugin built but renamed to '.jar.bak' then re-renamed incorrectly.
Related errors
- Invalid part of file
- Invalid part of policyName
- Form field file is required
- Invalid parameter of file
- No file part found in the request
AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14).
Data as JSON: /api/errors/4e50a85d3631cd23.
Report an issue: GitHub.