flowable/flowable-engine · error · FlowableImageException
CaseDiagramGenerator already closed
Error message
CaseDiagramGenerator already closed
What it means
DefaultDecisionRequirementsDiagramCanvas.generateImage throws FlowableImageException if generateImage is called after close() has been invoked. The canvas is single-use once closed; the internal diagram is no longer valid for image generation.
Solutions
- Call generateImage before close(), and close only once at the end.
- Create a new canvas (regenerate the diagram) for each image you need.
- Track the closed state in calling code and avoid reuse after close.
Example fix
// before
canvas.generateImage("png");
canvas.close();
canvas.generateImage("jpeg"); // throws
// after
canvas.generateImage("png");
canvas.generateImage("jpeg");
canvas.close(); Defensive patterns
Strategy: type-guard
Validate before calling
if (canvas == null) throw new IllegalStateException("canvas not created");
// track closed state in your wrapper before calling generateImage Type guard
boolean isOpen(DefaultDecisionRequirementsDiagramCanvas c) { return c != null && !c.isClosed(); } Try / catch
try {
InputStream is = canvas.generateImage("png");
} catch (FlowableImageException e) {
logger.error("Canvas closed or image failed", e);
} Prevention
- Generate all images before calling close()
- Create a fresh canvas per image format
- Encapsulate canvas lifecycle in try-with-resources-style scope
When it happens
Trigger: Calling canvas.generateImage(imageType) after canvas.close() has already been called, e.g. generating two image formats from one closed canvas or reusing a canvas across requests.
Common situations: Generating both PNG and SVG/other formats from the same canvas object; storing the canvas in a cache and reusing it after a lifecycle close; double-close in cleanup code followed by retry.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- DecisionRequirementsDiagramGenerator already closed
- CaseDiagramGenerator already closed
- Could not find graphic info for decision service
- Error while generating case image
- ProcessDiagramGenerator already closed
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/034eefdcbc6c3cbb.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-dmn-image-generator/src/main/java/org/flowable/dmn/image/impl/DefaultDecisionRequirementsDiagramCanvas.java:194
LABEL_FONT = new Font(labelFontName, Font.ITALIC, 10);
ANNOTATION_FONT = new Font(annotationFontName, Font.PLAIN, FONT_SIZE);
try {
DECISION_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/decision.png", customClassLoader));
} catch (IOException e) {
LOGGER.warn("Could not load image for decision requirements 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(decisionRequirementsDiagram, imageType, out);
return new ByteArrayInputStream(out.toByteArray());
} catch (IOException e) {
throw new FlowableImageException("Error while generating case image", e);
}
}
/**
* Generates an image of what currently is drawn on the canvas.
*
* Throws an {@link FlowableImageException} when {@link #close()} is already called.
*/
public BufferedImage generateBufferedImage(String imageType) {
if (closed) {
throw new FlowableImageException("DecisionRequirementsDiagramGenerator already closed");View on GitHub (pinned to d6d39ce1c6)