{"record":{"id":"a4f9b4c047c23127","repo":"chinabugotech/hutool","slug":"writer-is-closed","errorCode":null,"errorMessage":"Writer is closed!{}","messagePattern":"Writer is closed!(.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hutool-core/src/main/java/cn/hutool/core/io/AppendableWriter.java","lineNumber":94,"sourceCode":"\t\tappendable.append(CharBuffer.wrap(cbuf));\n\t}\n\n\t@Override\n\tpublic void flush() throws IOException {\n\t\tcheckNotClosed();\n\t\tif (flushable) {\n\t\t\t((Flushable) appendable).flush();\n\t\t}\n\t}\n\n\t/**\n\t * 检查Writer是否已经被关闭\n\t *\n\t * @throws IOException IO异常\n\t */\n\tprivate void checkNotClosed() throws IOException {\n\t\tif (closed) {\n\t\t\tthrow new IOException(\"Writer is closed!\" + this);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t\tif (false == closed) {\n\t\t\tflush();\n\t\t\tif (appendable instanceof Closeable) {\n\t\t\t\t((Closeable) appendable).close();\n\t\t\t}\n\t\t\tclosed = true;\n\t\t}\n\t}\n}\n","sourceCodeStart":76,"sourceCodeEnd":109,"githubUrl":"https://github.com/chinabugotech/hutool/blob/8870454b2a0c29cc6ffd31dcf5667c8ceb2fc442/hutool-core/src/main/java/cn/hutool/core/io/AppendableWriter.java#L76-L109","documentation":"AppendableWriter wraps any Appendable as a java.io.Writer. After close() runs, the private 'closed' flag becomes true and every subsequent write/append/flush call hits checkNotClosed(), which throws IOException(\"Writer is closed!\"). This is a standard use-after-close guard mirroring JDK stream behavior.","triggerScenarios":"Calling write(), append(), or flush() on an AppendableWriter instance after close() has already been invoked on it.","commonSituations":"Sharing a writer across methods where one path closes it in a finally/try-with-resources and another path later writes to it; pooling/reusing writer instances; closing in an exception handler then retrying the write.","solutions":["Do not reuse the AppendableWriter after close() — create a fresh instance backed by a new Appendable.","Track the lifecycle yourself and stop issuing write calls once you have closed the writer.","Move the close() to the true end of the writer's lifetime (outermost try-with-resources) so no write can follow it."],"exampleFix":"// before: writer closed inside loop then reused\nwriter.append(line);\nwriter.close();\nwriter.append(nextLine); // -> IOException: Writer is closed!\n\n// after: close once, after all writes\ntry (AppendableWriter w = new AppendableWriter(sb)) {\n    w.append(line);\n    w.append(nextLine);\n}","handlingStrategy":"validation","validationCode":"// Track ownership of the writer in the caller; never write after close.\n// AppendableWriter has no isOpen() accessor, so manage it externally:\nAppendableWriter w = new AppendableWriter(sb);\nboolean closed = false;\ntry {\n    w.append(line);\n} finally {\n    w.close();\n    closed = true;\n}\n// guard any deferred write\nif (!closed) { w.append(more); }","typeGuard":null,"tryCatchPattern":"try {\n    writer.append(text);\n} catch (IOException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Writer is closed!\")) {\n        // writer was already closed: create a new one or skip\n    } else {\n        throw e;\n    }\n}","preventionTips":["Treat close() as terminal — never issue writes after it.","Use try-with-resources so the close happens at the true end of the writer's life.","Do not share a single AppendableWriter across code paths that independently close it."],"tags":["io","writer","resource-lifecycle","use-after-close"],"backgroundTag":null,"analyzedSha":"8870454b2a0c29cc6ffd31dcf5667c8ceb2fc442","analyzedAt":"2026-08-14T04:01:12.892Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}