flowable/flowable-engine · error · org.activiti.engine.ActivitiException

Couldn't get file

Error message

Couldn't get file ${filePath}: ${e.getMessage()}

What it means

IoUtil.getFile resolves a classpath resource URL and converts it to a java.io.File via url.toURI(). Any failure — resource not found (URL null causing NPE), non-file URI schemes like jar:/bundle:, or URISyntaxException — becomes this ActivitiException with the path and cause message.

Solutions

  1. Confirm the resource exists on the classpath at exactly that path (Class.getResource returns non-null).
  2. Don't use getFile for resources inside jars — read via getResourceAsStream instead.
  3. Null-check the URL before conversion and fail with a clearer 'resource not found' message.
  4. Verify packaging: resources must be exploded directories for File-based access.

Example fix

// before
File f = IoUtil.getFile("templates/app.vm"); // inside a fat jar -> throws
// after
try (InputStream in = IoUtil.class.getClassLoader().getResourceAsStream("templates/app.vm")) {
    // read stream instead of File
}
Defensive patterns

Strategy: validation

Validate before calling

URL url = IoUtil.class.getClassLoader().getResource(filePath);
if (url == null) throw new IllegalStateException("Classpath resource not found: " + filePath);
if (!"file".equals(url.getProtocol())) throw new IllegalStateException("Resource inside jar, use getResourceAsStream: " + url);

Try / catch

try {
    File f = IoUtil.getFile(path);
} catch (ActivitiException e) {
    if (e.getMessage().startsWith("Couldn't get file")) { /* switch to stream-based loading */ }
}

Prevention

When it happens

Trigger: Calling IoUtil.getFile (directly or via writeStringToFile/readFileAsString/buffer) with a path that isn't a classpath resource, or one packaged inside a jar where the 'jar:' URI cannot be converted to a File.

Common situations: Typo'd resource path; resource not on the classpath; attempting File access on resources inside a Spring Boot fat jar or OSGi bundle.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/util/IoUtil.java:69

        byte[] buffer = new byte[(int) getFile(filePath).length()];
        BufferedInputStream inputStream = null;
        try {
            inputStream = new BufferedInputStream(new FileInputStream(getFile(filePath)));
            inputStream.read(buffer);
        } catch (Exception e) {
            throw new ActivitiException("Couldn't read file " + filePath + ": " + e.getMessage());
        } finally {
            IoUtil.closeSilently(inputStream);
        }
        return new String(buffer);
    }

    public static File getFile(String filePath) {
        URL url = IoUtil.class.getClassLoader().getResource(filePath);
        try {
            return new File(url.toURI());
        } catch (Exception e) {
            throw new ActivitiException("Couldn't get file " + filePath + ": " + e.getMessage());
        }
    }

    public static void writeStringToFile(String content, String filePath) {
        BufferedOutputStream outputStream = null;
        try {
            outputStream = new BufferedOutputStream(new FileOutputStream(getFile(filePath)));
            outputStream.write(content.getBytes());
            outputStream.flush();
        } catch (Exception e) {
            throw new ActivitiException("Couldn't write file " + filePath, e);
        } finally {
            IoUtil.closeSilently(outputStream);
        }
    }

    /**
     * Closes the given stream. The same as calling {@link InputStream#close()}, but errors while closing are silently ignored.

View on GitHub (pinned to d6d39ce1c6)