flowable/flowable-engine · error · FlowableImageException

CaseDiagramGenerator already closed

Error message

CaseDiagramGenerator already closed

What it means

DefaultCaseDiagramCanvas implements AutoCloseable; after close() is called the internal BufferedImage and state are disposed, so generateImage cannot render. The canvas is a single-shot generator: generate then close, never the reverse order.

Solutions

  1. Call generateImage() before close(); do not reuse the canvas after closing
  2. Create a new DefaultCaseDiagramCanvas (via CaseDiagramGenerator) for each export
  3. Restructure try-with-resources so all generateImage calls happen inside the block
  4. If multiple formats are needed, call generateImage once per format before closing

Example fix

// before
try (DefaultCaseDiagramCanvas canvas = ...) { }
InputStream is = canvas.generateImage("png");
// after
try (DefaultCaseDiagramCanvas canvas = ...) {
    InputStream is = canvas.generateImage("png");
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (canvas.isClosed()) { canvas = createNewCanvas(caseDefinition); }

Type guard

boolean usable(DefaultCaseDiagramCanvas c) { return !c.isClosed(); }

Try / catch

try {
    InputStream is = canvas.generateImage("png");
} catch (FlowableImageException e) {
    canvas = recreateCanvas();
    InputStream is = canvas.generateImage("png");
}

Prevention

When it happens

Trigger: Calling generateImage(String imageType) on a DefaultCaseDiagramCanvas instance after close() has already been invoked; reusing one canvas to export the diagram multiple times after the first close.

Common situations: try-with-resources closing the canvas then a later attempt to re-export; caching a canvas for repeated PNG/SVG exports; code that closes in a finally block but still calls generateImage afterwards.

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

Appendix: source

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

            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);
        }
    }

    /**
     * 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("CaseDiagramGenerator already closed");

View on GitHub (pinned to d6d39ce1c6)