{"record":{"id":"83ec98d015f23295","repo":"Tencent/VasSonic","slug":"getwriter-has-already-been-called-on-this-respon","errorCode":null,"errorMessage":"getWriter() has already been called on this response.","messagePattern":"getWriter\\(\\) has already been called on this response\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"sonic-java/src/main/java/com/github/tencent/HttpServletResponseCopier.java","lineNumber":22,"sourceCode":"import java.io.OutputStreamWriter;\nimport java.io.PrintWriter;\n\nimport javax.servlet.ServletOutputStream;\nimport javax.servlet.http.HttpServletResponse;\nimport javax.servlet.http.HttpServletResponseWrapper;\n\npublic class HttpServletResponseCopier extends HttpServletResponseWrapper {\n    private PrintWriter writer;\n    private ServletOutputStreamCopier copier;\n\n    public HttpServletResponseCopier(HttpServletResponse response) throws IOException {\n        super(response);\n    }\n\n    @Override\n    public ServletOutputStream getOutputStream() throws IOException {\n        if (writer != null) {\n            throw new IllegalStateException(\"getWriter() has already been called on this response.\");\n        }\n        copier = new ServletOutputStreamCopier();\n        return copier;\n    }\n\n    @Override\n    public PrintWriter getWriter() throws IOException {\n        if (copier != null) {\n            throw new IllegalStateException(\"getOutputStream() has already been called on this response.\");\n        }\n        if (writer == null) {\n            copier = new ServletOutputStreamCopier();\n            writer = new PrintWriter(new OutputStreamWriter(copier, getResponse().getCharacterEncoding()), true);\n        }\n        return writer;\n    }\n\n    @Override","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/Tencent/VasSonic/blob/59936beff656d4b5718ff6444d6c5e001a2c5231/sonic-java/src/main/java/com/github/tencent/HttpServletResponseCopier.java#L4-L40","documentation":"HttpServletResponseCopier is a response wrapper that captures response bodies. Per the Servlet spec, getOutputStream() and getWriter() are mutually exclusive; this wrapper enforces that by throwing IllegalStateException if getWriter() was already called.","triggerScenarios":"Calling getOutputStream() on the wrapped response after getWriter() has already been called on the same wrapper instance, e.g. a filter chain where one component writes text via getWriter() and a later one writes bytes via getOutputStream().","commonSituations":"Servlet filters that wrap the response and then delegate to servlets/frameworks with differing writing conventions; mixing JSP (getWriter) with binary output (getOutputStream); double-wrapping responses in nested filters.","solutions":["Standardize on one access method per response: use getWriter() for text or getOutputStream() for binary, never both","In filters, unwrap/replace the wrapper per branch so each stage has a consistent accessor","Track which accessor was used and route subsequent writes through it","If copying content, ensure the wrapper is created fresh for each response dispatch (include/forward)"],"exampleFix":"// before\nresponse.getWriter().write(\"log\");\nresponse.getOutputStream().write(bytes); // IllegalStateException\n// after\nPrintWriter w = response.getWriter();\nw.write(\"log\");\nw.flush(); // stick to a single accessor for the whole response","handlingStrategy":"try-catch","validationCode":"// track accessor usage before writing\nif (writerAlreadyObtained) {\n  use getWriter() path instead of getOutputStream();\n}","typeGuard":null,"tryCatchPattern":"try {\n  ServletOutputStream out = responseCopier.getOutputStream();\n  out.write(bytes);\n} catch (IllegalStateException e) {\n  // fall back to writing bytes through the Writer\n  responseCopier.getWriter().write(new String(bytes, StandardCharsets.ISO_8859_1));\n}","preventionTips":["Choose one response accessor per request lifecycle at the filter-design stage","Document that wrapped responses enforce Servlet getWriter/getOutputStream exclusivity","Avoid frameworks/handlers with mixed output conventions inside the same filter chain"],"tags":["servlet","java","illegal-state"],"backgroundTag":"invalid-state-transition","analyzedSha":"59936beff656d4b5718ff6444d6c5e001a2c5231","analyzedAt":"2026-09-08T10:27:05.448Z","contentChangedAt":"2026-09-08T10:27:05.448Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}