{"record":{"id":"cd907a5d61c52e05","repo":"flowable/flowable-engine","slug":"casediagramgenerator-already-closed","errorCode":null,"errorMessage":"CaseDiagramGenerator already closed","messagePattern":"CaseDiagramGenerator already closed","errorType":"exception","errorClass":"FlowableImageException","httpStatus":null,"severity":"error","filePath":"modules/flowable-cmmn-image-generator/src/main/java/org/flowable/cmmn/image/impl/DefaultCaseDiagramCanvas.java","lineNumber":222,"sourceCode":"            SERVICETASK_IMAGE = ImageIO.read(ReflectUtil.getResource(\"org/flowable/icons/serviceTask.png\", customClassLoader));\n            CASETASK_IMAGE = ImageIO.read(ReflectUtil.getResource(\"org/flowable/icons/caseTask.png\", customClassLoader));\n            PROCESSTASK_IMAGE = ImageIO.read(ReflectUtil.getResource(\"org/flowable/icons/processTask.png\", customClassLoader));\n            DECISIONTASK_IMAGE = ImageIO.read(ReflectUtil.getResource(\"org/flowable/icons/decisionTask.png\", customClassLoader));\n            SENDEVENTTASK_IMAGE = ImageIO.read(ReflectUtil.getResource(\"org/flowable/icons/sendEventTask.png\", customClassLoader));\n\n        } catch (IOException e) {\n            LOGGER.warn(\"Could not load image for case diagram creation: {}\", e.getMessage());\n        }\n    }\n\n    /**\n     * Generates an image of what currently is drawn on the canvas.\n     *\n     * Throws an {@link FlowableImageException} when {@link #close()} is already called.\n     */\n    public InputStream generateImage(String imageType) {\n        if (closed) {\n            throw new FlowableImageException(\"CaseDiagramGenerator already closed\");\n        }\n\n        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {\n            ImageIO.write(caseDiagram, imageType, out);\n            return new ByteArrayInputStream(out.toByteArray());\n        } catch (IOException e) {\n            throw new FlowableImageException(\"Error while generating case image\", e);\n        }\n    }\n\n    /**\n     * Generates an image of what currently is drawn on the canvas.\n     *\n     * Throws an {@link FlowableImageException} when {@link #close()} is already called.\n     */\n    public BufferedImage generateBufferedImage(String imageType) {\n        if (closed) {\n            throw new FlowableImageException(\"CaseDiagramGenerator already closed\");","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-image-generator/src/main/java/org/flowable/cmmn/image/impl/DefaultCaseDiagramCanvas.java#L204-L240","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Call generateImage() before close(); do not reuse the canvas after closing","Create a new DefaultCaseDiagramCanvas (via CaseDiagramGenerator) for each export","Restructure try-with-resources so all generateImage calls happen inside the block","If multiple formats are needed, call generateImage once per format before closing"],"exampleFix":"// before\ntry (DefaultCaseDiagramCanvas canvas = ...) { }\nInputStream is = canvas.generateImage(\"png\");\n// after\ntry (DefaultCaseDiagramCanvas canvas = ...) {\n    InputStream is = canvas.generateImage(\"png\");\n}","handlingStrategy":"try-catch","validationCode":"if (canvas.isClosed()) { canvas = createNewCanvas(caseDefinition); }","typeGuard":"boolean usable(DefaultCaseDiagramCanvas c) { return !c.isClosed(); }","tryCatchPattern":"try {\n    InputStream is = canvas.generateImage(\"png\");\n} catch (FlowableImageException e) {\n    canvas = recreateCanvas();\n    InputStream is = canvas.generateImage(\"png\");\n}","preventionTips":["Generate images strictly before close()","Use try-with-resources with all generation inside the block","Create a new canvas per export instead of caching instances"],"tags":["cmmn","diagram","lifecycle","closed-resource"],"backgroundTag":"invalid-state-transition","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}