flowable/flowable-engine · warning

Could not load image for process diagram creation

Error message

Could not load image for process diagram creation: {}

What it means

WARN logged by DefaultProcessDiagramCanvas.initialize when reading the bundled Flowable icon PNGs (message/signal throw/catch) fails with an IOException. The canvas continues with null image fields, so generated process diagrams will be missing those icon graphics. Usually indicates the classpath resources were not packaged or the custom classloader cannot see them.

Solutions

  1. Ensure flowable-image-generator jar (and its org/flowable/icons resources) is on the runtime classpath
  2. Check build config that resource filtering/exclusion is not stripping *.png files (e.g. maven resource filtering of binaries)
  3. Pass the correct customClassLoader or null to use the default classloader
  4. Ignore the warning if icons are not needed — diagrams still generate without them

Example fix

<!-- before: maven filtering corrupts binaries -->
<resource><directory>src/main/resources</directory><filtering>true</filtering></resource>
<!-- after -->
<resource><directory>src/main/resources</directory><filtering>false</filtering><excludes><exclude>**/*.png</exclude></excludes></resource>
Defensive patterns

Strategy: fallback

Validate before calling

// verify icons are resolvable before generating diagrams
try (InputStream is = getClass().getClassLoader()
        .getResourceAsStream("org/flowable/icons/signal.png")) {
    if (is == null) throw new IllegalStateException("Flowable icon resources missing from classpath");
}

Try / catch

try {
    ProcessDiagramGenerator gen = new DefaultProcessDiagramGenerator();
    diagram = gen.generateDiagram(model, "png", activeActivityIds);
} catch (FlowableException e) {
    LOGGER.warn("Diagram generation degraded: {}", e.getMessage());
    diagram = null; // proceed without diagram
}

Prevention

When it happens

Trigger: Constructor -> initialize calls ImageIO.read(ReflectUtil.getResource(...)) for each icon; IOException occurs when resource bytes are unreadable/missing or the provided customClassLoader cannot locate org/flowable/icons/*.png.

Common situations: See trigger scenarios.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable-image-generator/src/main/java/org/flowable/image/impl/DefaultProcessDiagramCanvas.java:252

            DMN_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/dmnTask.png", customClassLoader));
            CAMEL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/camelTask.png", customClassLoader));
            HTTP_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/httpTask.png", customClassLoader));

            TIMER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/timer.png", customClassLoader));
            COMPENSATE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate-throw.png", customClassLoader));
            COMPENSATE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate.png", customClassLoader));
            CONDITIONAL_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/conditional.png", customClassLoader));
            ERROR_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error-throw.png", customClassLoader));
            ERROR_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error.png", customClassLoader));
            ESCALATION_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/escalation-throw.png", customClassLoader));
            ESCALATION_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/escalation.png", customClassLoader));
            MESSAGE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message-throw.png", customClassLoader));
            MESSAGE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message.png", customClassLoader));
            SIGNAL_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal-throw.png", customClassLoader));
            SIGNAL_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal.png", customClassLoader));
            
        } catch (IOException e) {
            LOGGER.warn("Could not load image for process 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("ProcessDiagramGenerator already closed");
        }

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

View on GitHub (pinned to d6d39ce1c6)