flowable/flowable-engine · error · FlowableImageException

DecisionRequirementsDiagramGenerator already closed

Error message

DecisionRequirementsDiagramGenerator already closed

What it means

DefaultDecisionRequirementsDiagramCanvas.generateBufferedImage throws FlowableImageException when the canvas has been closed, mirroring generateImage. Once closed, no BufferedImage can be produced from the canvas.

Solutions

  1. Call generateBufferedImage before close().
  2. Regenerate the diagram into a fresh canvas if a buffered image is needed later.
  3. Restructure code so all image extraction happens inside the canvas's open lifecycle.

Example fix

// before
canvas.close();
BufferedImage img = canvas.generateBufferedImage("png"); // throws
// after
BufferedImage img = canvas.generateBufferedImage("png");
canvas.close();
Defensive patterns

Strategy: type-guard

Validate before calling

if (!canvasOpen) throw new IllegalStateException("Canvas already closed; regenerate the diagram");

Type guard

boolean canRender(DefaultDecisionRequirementsDiagramCanvas c) { return c != null && !c.isClosed(); }

Try / catch

try {
    BufferedImage img = canvas.generateBufferedImage("png");
} catch (FlowableImageException e) {
    logger.error("Canvas closed before buffered image generation", e);
}

Prevention

When it happens

Trigger: Calling generateBufferedImage(imageType) after close() was called on the same canvas instance.

Common situations: Requesting a BufferedImage after already exporting an InputStream via generateImage followed by close(); reusing a cached canvas after its lifecycle ended.

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

Appendix: source

Thrown at modules/flowable-dmn-image-generator/src/main/java/org/flowable/dmn/image/impl/DefaultDecisionRequirementsDiagramCanvas.java:212

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

        // Try to remove white space
        minX = (minX <= 5) ? 5 : minX;
        minY = (minY <= 5) ? 5 : minY;
        BufferedImage imageToSerialize = decisionRequirementsDiagram;
        imageToSerialize = decisionRequirementsDiagram.getSubimage(minX - 5, minY - 5, canvasWidth - minX + 5, canvasHeight - minY + 5);
        return imageToSerialize;
    }

    /**
     * Closes the canvas which disallows further drawing and releases graphical resources.
     */
    public void close() {
        g.dispose();
        closed = true;
    }

View on GitHub (pinned to d6d39ce1c6)