flowable/flowable-engine · warning

Could not load image for case diagram creation

Error message

Could not load image for case diagram creation: {}

What it means

During static initialization of DefaultCaseDiagramCanvas, the icon PNGs (caseTask.png, processTask.png, decisionTask.png, sendEventTask.png) are loaded from the classpath via ImageIO.read. If any of them cannot be read or found, an IOException is caught and only a warning is logged, leaving the static image fields null. Diagram generation will then produce task nodes without icons or fail later when drawing them.

Solutions

  1. Verify the flowable-cmmn-image-generator jar contains org/flowable/icons/*.png and that your classloader can load them (unzip -l the jar).
  2. Disable Maven/Gradle resource filtering on binary files (png) or add nonFilteredFileExtensions for png.
  3. If using a custom ClassLoader, pass one that can see the flowable jar resources, or null to use the default.
  4. Check the underlying e.getMessage() in the log: 'Can't read input file' vs 'not found' tells you whether the resource is missing or corrupt.
  5. Ensure icons are read before any ImageIO Usages are affected: pin a standard flowable release instead of a custom repack.

Example fix

// before (pom.xml resource filtering mangles PNGs)
<resource><directory>src/main/resources</directory><filtering>true</filtering></resource>
// after
<resource><directory>src/main/resources</directory><filtering>false</filtering></resource>
Defensive patterns

Strategy: fallback

Validate before calling

try (var in = getClass().getClassLoader().getResourceAsStream("org/flowable/icons/caseTask.png")) {
    if (in == null) throw new IllegalStateException("flowable icon resources missing from classpath");
}

Type guard

boolean iconsAvailable(ClassLoader cl) {
    return cl.getResource("org/flowable/icons/caseTask.png") != null
        && cl.getResource("org/flowable/icons/processTask.png") != null
        && cl.getResource("org/flowable/icons/decisionTask.png") != null
        && cl.getResource("org/flowable/icons/sendEventTask.png") != null;
}

Try / catch

try {
    new DefaultCaseDiagramGenerator().generateDiagram(model, imageType, activityFont);
} catch (RuntimeException e) {
    LOGGER.warn("Diagram generation unavailable (icons not loadable), skipping diagram", e);
    return null; // fallback: proceed without diagram
}

Prevention

When it happens

Trigger: Creating a DefaultCaseDiagramCanvas (or generating a CMMN case diagram) when org/flowable/icons/*.png resources are not resolvable through the provided customClassLoader, or the classpath resource is corrupt/unreadable so ImageIO.read throws IOException.

Common situations: Shaded/minified jars or custom classloaders (e.g. OSGi, Spring Boot fat jar with filtering) that exclude or corrupt icon resources; build resource filtering mangling PNG binaries; running with an incomplete flowable-image-generator dependency or repackaged flowable jars missing icon resources.

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/6b5856f428ef2208. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable-cmmn-image-generator/src/main/java/org/flowable/cmmn/image/impl/DefaultCaseDiagramCanvas.java:211

        g.setFont(font);
        this.fontMetrics = g.getFontMetrics();

        LABEL_FONT = new Font(labelFontName, Font.ITALIC, 10);
        ANNOTATION_FONT = new Font(annotationFontName, Font.PLAIN, FONT_SIZE);

        try {
            TIMER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/timer.png", customClassLoader));
            USERLISTENER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/user.png", customClassLoader));
            VARIABLELISTENER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/variablelistener.png", customClassLoader));
            USERTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/userTask.png", customClassLoader));
            SERVICETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/serviceTask.png", customClassLoader));
            CASETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/caseTask.png", customClassLoader));
            PROCESSTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/processTask.png", customClassLoader));
            DECISIONTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/decisionTask.png", customClassLoader));
            SENDEVENTTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/sendEventTask.png", customClassLoader));

        } catch (IOException e) {
            LOGGER.warn("Could not load image for case diagram creation: {}", e.getMessage());
        }
    }

    /**
     * Generates an image of what currently is drawn on the canvas.
     *
     * Throws an {@link FlowableImageException} when {@link #close()} is already called.
     */
    public InputStream generateImage(String imageType) {
        if (closed) {
            throw new FlowableImageException("CaseDiagramGenerator already closed");
        }

        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            ImageIO.write(caseDiagram, imageType, out);
            return new ByteArrayInputStream(out.toByteArray());
        } catch (IOException e) {
            throw new FlowableImageException("Error while generating case image", e);

View on GitHub (pinned to d6d39ce1c6)