flowable/flowable-engine · error · FlowableException

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

Error message

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

What it means

IoUtil.getFile resolves a path through the classloader (ClassLoader.getResource) and converts the URL to a File. Any failure — including a null URL when the resource is missing — is wrapped in FlowableException 'Couldn't get file <path>: <message>'.

Source

Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/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 FlowableException("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 FlowableException("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 FlowableException("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)

Solutions

  1. Verify the resource path exists and is on the classpath (check the built jar/war contents)
  2. Fix typos and ensure the file is under src/main/resources so it is packaged
  3. For resources inside jars, stream them with getResourceAsStream instead of converting to File
  4. Call getFile only for resources that unpack to real filesystem files

Example fix

// before
File f = IoUtil.getFile("processes/order.bpmn20.xml");
// after
try (InputStream in = getClass().getClassLoader().getResourceAsStream("processes/order.bpmn20.xml")) {
    if (in == null) throw new IllegalStateException("resource missing from classpath");
    byte[] bytes = IoUtil.readInputStream(in, "processes/order.bpmn20.xml");
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (getClass().getClassLoader().getResource(filePath) == null) {
    throw new IllegalStateException("Resource not found on classpath: " + filePath);
}

Try / catch

try {
    File f = IoUtil.getFile(filePath);
} catch (FlowableException e) {
    logger.error("Resource lookup failed for {}: {}", filePath, e.getMessage());
}

Prevention

When it happens

Trigger: Calling getFile (directly or via readFileAsString/writeStringToFile) with a path that does not exist on the classpath, or a resource inside a jar (URL scheme like jar:file:... that File(...) cannot convert, e.g. URISyntaxException).

Common situations: Typo'd resource path; resource not included in the built artifact (missing from src/main/resources); attempting to access resources inside nested jars in Spring Boot fat jars; running from a packaged WAR.

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/630e20ea1dcf1bf4. Report an issue: GitHub.