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

  1. Call generateImage before close(), and close only once at the end.
  2. Create a new canvas (regenerate the diagram) for each image you need.
  3. 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

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


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)