flowable/flowable-engine · error · FlowableIllegalArgumentException

Multipart request is required

Error message

Multipart request is required

What it means

Thrown by AppDeploymentCollectionResource.uploadDeployment when the HTTP request handling the app deployment upload is not a MultipartHttpServletRequest. Flowable requires multipart/form-data because it reads the uploaded .app/.zip file from the multipart file map. A plain POST body (raw bytes, JSON, or a missing/incorrect Content-Type) triggers this.

Source

Thrown at modules/flowable-app-engine-rest/src/main/java/org/flowable/app/rest/service/api/repository/AppDeploymentCollectionResource.java:156

            "App Deployments" }, consumes = "multipart/form-data", produces = "application/json", notes = "The request body should contain data of type multipart/form-data. There should be exactly one file in the request, any additional files will be ignored. The deployment name is the name of the file-field passed in. Make sure the file-name ends with .app, .zip or .bar.",
            code = 201)
    @ApiResponses(value = {
            @ApiResponse(code = 201, message = "Indicates the app deployment was created."),
            @ApiResponse(code = 400, message = "Indicates there was no content present in the request body or the content mime-type is not supported for app deployment. The status-description contains additional information.")
    })
    @ApiImplicitParams({
        @ApiImplicitParam(name="file", paramType = "form", dataType = "java.io.File")
    })
    @PostMapping(value = "/app-repository/deployments", produces = "application/json", consumes = "multipart/form-data")
    @ResponseStatus(HttpStatus.CREATED)
    public AppDeploymentResponse uploadDeployment(@ApiParam(name = "tenantId") @RequestParam(value = "tenantId", required = false) String tenantId, HttpServletRequest request) {

        if (restApiInterceptor != null) {
            restApiInterceptor.executeNewDeploymentForTenantId(tenantId);
        }
        
        if (!(request instanceof MultipartHttpServletRequest)) {
            throw new FlowableIllegalArgumentException("Multipart request is required");
        }
        
        String queryString = request.getQueryString();
        Map<String, String> decodedQueryStrings = splitQueryString(queryString);

        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;

        if (multipartRequest.getFileMap().size() == 0) {
            throw new FlowableIllegalArgumentException("Multipart request with file content is required");
        }

        MultipartFile file = multipartRequest.getFileMap().values().iterator().next();

        try {
            AppDeploymentBuilder deploymentBuilder = appRepositoryService.createDeployment();
            String fileName = file.getOriginalFilename();
            if (StringUtils.isEmpty(fileName) || !(fileName.endsWith(".app") || fileName.toLowerCase().endsWith(".bar") || fileName.toLowerCase().endsWith(".zip"))) {
                fileName = file.getName();

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Send the file as multipart/form-data, e.g. curl -F "file=@my.app" ".../app-repository/deployments?deploymentName=MyDeploy".
  2. Verify Content-Type: multipart/form-data with boundary is set (do not set it manually for FormData in browsers — let the client set the boundary).
  3. Ensure Spring's MultipartResolver is enabled so the request is wrapped as MultipartHttpServletRequest.
  4. Check proxies/gateways preserve multipart bodies and headers.

Example fix

// before
curl -X POST -H 'Content-Type: application/octet-stream' --data-binary @my.app ...
// after
curl -X POST -F 'file=@my.app' 'http://host/flowable-rest/app-repository/deployments?deploymentName=my-deploy'
Defensive patterns

Strategy: validation

Validate before calling

# Ensure the request is multipart before sending
def upload_app(url, file_path):
    with open(file_path, 'rb') as f:
        files = {'file': (file_path, f, 'application/octet-stream')}
        return requests.post(url, files=files)  # requests sets multipart/form-data

Type guard

// Server-side guard mirrors the library check
if (!(request instanceof MultipartHttpServletRequest)) {
    throw new FlowableIllegalArgumentException("Multipart request is required");
}

Try / catch

try {
    ResponseEntity<String> resp = restTemplate.postForEntity(url, multipartEntity, String.class);
} catch (HttpClientErrorException.BadRequest e) {
    if (e.getResponseBodyAsString().contains("Multipart request is required")) {
        // resend as multipart/form-data with a file part
    }
}

Prevention

When it happens

Trigger: POST /app-repository/deployments (optionally /{tenantId}) without multipart/form-data Content-Type, without a file part, or through a client/proxy that does not forward multipart correctly.

Common situations: curl -d instead of curl -F; fetch/axios sending FormData without proper content type; Spring MultipartResolver not configured so the wrapper is absent; gateways that strip or rewrite Content-Type.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/055946308b9c9b99. Report an issue: GitHub.