{"record":{"id":"69413a62ee845188","repo":"quarkusio/quarkus","slug":"stream-is-closed-69413a","errorCode":null,"errorMessage":"Stream is closed","messagePattern":"Stream is closed","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"independent-projects/vertx-utils/src/main/java/io/quarkus/vertx/utils/VertxOutputStream.java","lineNumber":173,"sourceCode":"        write(new byte[] { (byte) b }, 0, 1);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public void write(final byte[] b) throws IOException {\n        write(b, 0, b.length);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    public void write(final byte[] b, final int off, final int len) throws IOException {\n        if (len < 1) {\n            return;\n        }\n        if (closed) {\n            throw new IOException(\"Stream is closed\");\n        }\n\n        int rem = len;\n        int idx = off;\n        try {\n            while (rem > 0) {\n                final int written = appendBuffer.append(b, idx, rem);\n                if (written < rem) {\n                    writeBlocking(appendBuffer.clear(), false);\n                }\n                rem -= written;\n                idx += written;\n            }\n        } catch (Exception e) {\n            throw new IOException(e);\n        }\n    }\n","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/independent-projects/vertx-utils/src/main/java/io/quarkus/vertx/utils/VertxOutputStream.java#L155-L191","documentation":"VertxOutputStream tracks a closed flag; once close() has been called, any further write(byte[],int,int) throws IOException('Stream is closed'). Writing to a finished HTTP response is a programming error, so the stream fails fast rather than silently dropping bytes.","triggerScenarios":"Calling write()/writeBlocking() after close(); writing from a deferred callback or subscription that fires after the response completed; double-handling of the response (e.g. returning a value after already streaming it).","commonSituations":"Reactive pipelines (Mutiny/CompletionStage) continuing to emit after the resource finished; framework filters that close the stream then downstream code writes more; error-handling paths that write to an already-committed/closed response.","solutions":["Ensure close() is only called after all writes are complete and nothing else will write afterwards","Guard producers with the stream's lifecycle (stop subscription on close or onResponseClosed)","Restructure handlers so a response is either streamed or returned normally, never both","Check/capture a `closed` flag in your writer loop before each write"],"exampleFix":"// before\nout.close();\nout.write(trailer); // IOException: Stream is closed\n// after\nout.write(trailer);\nout.close();","handlingStrategy":"type-guard","validationCode":"if (out == null || out.isClosed()) { /* skip write */ }","typeGuard":"boolean writable(VertxOutputStream s) {\n    return s != null && !s.isClosed();\n}","tryCatchPattern":"try {\n    out.writeBlocking(data);\n} catch (IOException e) {\n    if (\"Stream is closed\".equals(e.getMessage())) {\n        logger.debug(\"Write after close skipped\");\n    } else {\n        throw e;\n    }\n}","preventionTips":["Write-then-close in a single owner; never write after close() is invoked","Wire Mutiny/CompletionStage continuations to stop when the stream closes","Avoid paths that both stream the response and return a body afterwards"],"tags":["io","vertx","streaming","lifecycle"],"backgroundTag":"stream-already-closed","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}