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
- Ensure flowable-image-generator jar (and its org/flowable/icons resources) is on the runtime classpath
- Check build config that resource filtering/exclusion is not stripping *.png files (e.g. maven resource filtering of binaries)
- Pass the correct customClassLoader or null to use the default classloader
- 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
- Keep flowable-image-generator jar intact (do not strip resources in fat jars)
- Disable binary resource filtering in build configs
- Treat diagram generation as optional and degrade gracefully
- Test diagram generation in the same classloading environment as production (app servers/OSGi)
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
- Could not load image for case diagram creation
- BPMN XSD could not be found
- Cannot read default EL properties
- Class not found
- Class has not been found.
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)