{"record":{"id":"e305cf4c3c001bc2","repo":"golang/go","slug":"lzw-reader-writer-is-closed","errorCode":null,"errorMessage":"lzw: reader/writer is closed","messagePattern":"lzw: reader/writer is closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/compress/lzw/reader.go","lineNumber":226,"sourceCode":"\t\t\t\t// Undo the d.hi++ a few lines above, so that (1) we maintain\n\t\t\t\t// the invariant that d.hi < d.overflow, and (2) d.hi does not\n\t\t\t\t// eventually overflow a uint16.\n\t\t\t\tr.hi--\n\t\t\t} else {\n\t\t\t\tr.width++\n\t\t\t\tr.overflow = 1 << r.width\n\t\t\t}\n\t\t}\n\t\tif r.o >= flushBuffer {\n\t\t\tbreak\n\t\t}\n\t}\n\t// Flush pending output.\n\tr.toRead = r.output[:r.o]\n\tr.o = 0\n}\n\nvar errClosed = errors.New(\"lzw: reader/writer is closed\")\n\n// Close closes the [Reader] and returns an error for any future read operation.\n// It does not close the underlying [io.Reader].\nfunc (r *Reader) Close() error {\n\tr.err = errClosed // in case any Reads come along\n\treturn nil\n}\n\n// Reset clears the [Reader]'s state and allows it to be reused again\n// as a new [Reader].\nfunc (r *Reader) Reset(src io.Reader, order Order, litWidth int) {\n\t*r = Reader{}\n\tr.init(src, order, litWidth)\n}\n\n// NewReader creates a new [io.ReadCloser].\n// Reads from the returned [io.ReadCloser] read and decompress data from r.\n// If r does not also implement [io.ByteReader],","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/compress/lzw/reader.go#L208-L244","documentation":"Defined as the package-level errClosed sentinel and assigned to r.err by Reader.Close(). Any subsequent Read returns it. Close is idempotent for the reader state, but the error is sticky — it does not auto-reset.","triggerScenarios":"Calling Read on an lzw.Reader (or Write on an lzw.Writer) after Close has been invoked. Common with deferred Close followed by a drain loop, or when a Reader is pooled and a goroutine races ahead of the close.","commonSituations":"Using `defer r.Close()` and then reading in a separate goroutine; reading from a sync.Pool'd Reader that the previous owner forgot to Reset; misuse of io.Copy after Close in a cleanup path.","solutions":["Move the drain (io.Copy / io.ReadAll) ahead of Close, or drop the defer and close explicitly after draining.","If reusing a pooled Reader, call Reset(src, order, litWidth) before each use — Reset clears the error.","Check the returned error: errors.Is(err, lzw's closed sentinel) where applicable, or simply treat any read-after-close as a use-after-free bug in your code.","Use sync.Pool with care: never return a closed Reader to the pool."],"exampleFix":"// before\ndefer r.Close()\ngo func() { io.Copy(dst, r) }() // races with defer\n\n// after: drain before close\nif _, err := io.Copy(dst, r); err != nil { return err }\nif err := r.Close(); err != nil { return err }","handlingStrategy":"try-catch","validationCode":"// Ensure Read happens-before Close in your goroutine layout.\n// A pre-flight check is just: track an explicit closed flag.\ntype safeLZW struct {\n    r       *lzw.Reader\n    closed  atomic.Bool\n}\n\nfunc (s *safeLZW) Read(p []byte) (int, error) {\n    if s.closed.Load() { return 0, errors.New(\"reader closed\") }\n    return s.r.Read(p)\n}","typeGuard":null,"tryCatchPattern":"n, err := r.Read(buf)\nif err != nil && err.Error() == \"lzw: reader/writer is closed\" {\n    // Use-after-close bug: drain before Close, or Reset before reuse.\n    return nil\n}","preventionTips":["Never defer Close before the drain loop in a goroutine.","For pooled readers, always Reset before use and never return a closed reader to the pool.","Treat read-after-close as a bug in your code, not a runtime condition to handle."],"tags":["lzw","compression","lifecycle","use-after-close","reader"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}