{"record":{"id":"4e323c013878a0b4","repo":"quarkusio/quarkus","slug":"quarkusmultipartresponsedecoder-was-destroyed-alre","errorCode":null,"errorMessage":"QuarkusMultipartResponseDecoder was destroyed already","messagePattern":"QuarkusMultipartResponseDecoder was destroyed already","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartResponseDecoder.java","lineNumber":252,"sourceCode":"        currentStatus = MultiPartStatus.HEADERDELIMITER;\n\n        try {\n            if (this.response instanceof HttpContent) {\n                // Offer automatically if the given request is als type of HttpContent\n                // See #1089\n                offer((HttpContent) this.response);\n            } else {\n                parseBody();\n            }\n        } catch (Throwable e) {\n            destroy();\n            PlatformDependent.throwException(e);\n        }\n    }\n\n    private void checkDestroyed() {\n        if (destroyed) {\n            throw new IllegalStateException(QuarkusMultipartResponseDecoder.class.getSimpleName()\n                    + \" was destroyed already\");\n        }\n    }\n\n    /**\n     * True if this request is a Multipart request\n     *\n     * @return True if this request is a Multipart request\n     */\n    public boolean isMultipart() {\n        checkDestroyed();\n        return true;\n    }\n\n    /**\n     * Set the amount of bytes after which read bytes in the buffer should be discarded.\n     * Setting this lower gives lower memory usage but with the overhead of more memory copies.\n     * Use {@code 0} to disable it.","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartResponseDecoder.java#L234-L270","documentation":"checkDestroyed() throws IllegalStateException when any public operation (offer, hasNext, next, getBodyHttpDatas, getBodyHttpData, isMultipart, etc.) is called after destroy() has run. The decoder is a stateful, closeable object and cannot be reused after being torn down.","triggerScenarios":"Calling offer()/hasNext()/next()/getBodyHttpData(s) on a decoder instance after destroy() was called — typically by continuing to use a decoder stored in a field after response completion, or on error paths that call destroy().","commonSituations":"Reusing a cached decoder across requests; async callbacks firing after the response was cleaned up; decoding an errored response that triggered destroy() internally (e.g. missing boundary) and then probing its state.","solutions":["Create a new QuarkusMultipartResponseDecoder per response; never reuse or cache instances","Guard usage with isDestroyed() (or your own flag) before further calls","Ensure lifecycle code does not call destroy() while more chunks may still arrive","Restructure async handlers so the decoder reference is dropped once the response completes"],"exampleFix":"// before\nthis.decoder.offer(chunk); // decoder may already be destroyed\n// after\nif (!decoder.isDestroyed()) {\n    decoder.offer(chunk);\n} else {\n    decoder = new QuarkusMultipartResponseDecoder(response, factory, charset);\n    decoder.offer(chunk);\n}","handlingStrategy":"type-guard","validationCode":"if (decoder.isDestroyed()) { decoder = createNewDecoder(); }","typeGuard":"boolean usable(QuarkusMultipartResponseDecoder d) { return d != null && !d.isDestroyed(); }","tryCatchPattern":"try { decoder.offer(chunk); } catch (IllegalStateException e) { if (e.getMessage().contains(\"destroyed\")) { decoder = createNewDecoder(); decoder.offer(chunk); } else throw e; }","preventionTips":["One decoder instance per response; never cache across requests","Null out the decoder reference in the response-completion handler","Wrap multi-callback handling in a lifecycle owner that checks destroyed state"],"tags":["lifecycle","multipart","client","state"],"backgroundTag":"decoder-already-destroyed","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}