flowable/flowable-engine · error · FlowableIllegalArgumentException

File must be of type .bpmn20.xml, .bpmn, .bar or .zip

Error message

File must be of type .bpmn20.xml, .bpmn, .bar or .zip

What it means

FlowableIllegalArgumentException thrown when the uploaded file's name does not have one of the accepted extensions: .bpmn20.xml, .bpmn, .bar or .zip. uploadDeployment dispatches on the file extension (xml is added directly, zip/bar via ZipInputStream) and rejects anything else.

Solutions

  1. Rename the file to use an accepted extension, e.g. process.bpmn20.xml or process.bpmn
  2. Package multiple resources into a .bar or .zip archive and upload that
  3. Export the BPMN XML from the modeler rather than uploading the model source file
  4. Check the file name is URL-decoded correctly (the extension check uses the decoded query/name)

Example fix

// before
mv process.xml process.xml  // rejected: unsupported extension
curl -F "file=@process.xml" ...
// after
mv process.xml process.bpmn20.xml
curl -F "file=@process.bpmn20.xml" ...
Defensive patterns

Strategy: validation

Validate before calling

const ok = /\.(bpmn20\.xml|bpmn|bar|zip)$/i.test(filename);
if (!ok) throw new Error(`Rename ${filename} to .bpmn20.xml, .bpmn, .bar or .zip before uploading`);

Type guard

function isDeployableFile(name) {
  return /\.(bpmn20\.xml|bpmn|bar|zip)$/i.test(name);
}

Try / catch

try {
    deploy(file)
} catch (BadRequest e) {
    if (e.message?.includes('File must be of type')) {
        throw new Error(`Unsupported extension: ${file.name}. Use .bpmn20.xml, .bpmn, .bar or .zip`);
    }
}

Prevention

When it happens

Trigger: Uploading a file named e.g. process.xml, diagram.png, model.bpmnxml, or a BPMN file without any extension to POST /repository/deployments; the extension check happens before any parsing.

Common situations: Files exported with a .xml extension instead of .bpmn20.xml, Camel-cased or missing extensions, renamed files, or uploading the whole model JSON from the Flowable modeler instead of the exported BPMN XML.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/repository/DeploymentCollectionResource.java:209

            if (StringUtils.isEmpty(fileName) || !(fileName.endsWith(".bpmn20.xml") || fileName.endsWith(".bpmn") || fileName.toLowerCase().endsWith(".bar") || fileName.toLowerCase().endsWith(".zip"))) {

                fileName = file.getName();
            }

            if (fileName.endsWith(".bpmn20.xml") || fileName.endsWith(".bpmn")) {
            	try (final InputStream fileInputStream = file.getInputStream()) {
                    deploymentBuilder.addInputStream(fileName, fileInputStream);
                }
            	
            } else if (fileName.toLowerCase().endsWith(".bar") || fileName.toLowerCase().endsWith(".zip")) {
            	try (InputStream fileInputStream = file.getInputStream();
                        ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) {
            		
            		deploymentBuilder.addZipInputStream(zipInputStream);
            	}

            } else {
                throw new FlowableIllegalArgumentException("File must be of type .bpmn20.xml, .bpmn, .bar or .zip");
            }

            if (!decodedQueryStrings.containsKey("deploymentName") || StringUtils.isEmpty(decodedQueryStrings.get("deploymentName"))) {
                String fileNameWithoutExtension = fileName.split("\\.")[0];

                if (StringUtils.isNotEmpty(fileNameWithoutExtension)) {
                    fileName = fileNameWithoutExtension;
                }

                deploymentBuilder.name(fileName);
                
            } else {
                deploymentBuilder.name(decodedQueryStrings.get("deploymentName"));
            }

            if (decodedQueryStrings.containsKey("deploymentKey") && StringUtils.isNotEmpty(decodedQueryStrings.get("deploymentKey"))) {
                deploymentBuilder.key(decodedQueryStrings.get("deploymentKey"));
            }

View on GitHub (pinned to d6d39ce1c6)