golang/go · error
flate: closed writer
Error message
flate: closed writer
What it means
errWriterClosed is the sentinel set on the compressor after a successful close() (which flushes, writes the EOF stored block, and detaches the writer via d.w.reset(nil)). Any subsequent Write, Flush, or write-after-close observes d.err != nil and returns it. close() itself is idempotent: a second Close returns nil immediately, so the error surfaces specifically on writes to an already-closed Writer, not on duplicate closes.
Source
Thrown at src/compress/flate/deflate.go:783
}
if d.compressionLevel.chain == 0 {
return
}
s := d.state
s.chainHead = -1
clear(s.hashHead[:])
clear(s.hashPrev[:])
s.hashOffset = 1
s.index = 0
d.blockStart, d.byteAvailable = 0, false
d.tokens.Reset()
s.length = minMatchLength - 1
s.offset = 0
s.literalCounter = 0
s.maxInsertIndex = 0
}
var errWriterClosed = errors.New("flate: closed writer")
// close flushes any uncompressed data and writes an EOF block.
func (d *compressor) close() error {
if d.err == errWriterClosed {
return nil
}
if d.err != nil {
return d.err
}
d.sync = true
d.step(d)
if d.err != nil {
return d.err
}
if d.w.writeStoredHeader(0, true); d.w.err != nil {
return d.w.err
}
d.w.flush()View on GitHub (pinned to b6b368adc5)
Solutions
- Stop writing to the Writer after Close; treat Close as terminal for that instance.
- If you need to compress again, create a new Writer (or use Reset to re-initialize a reusable compressor with a fresh underlying writer).
- Guard writes with a closed flag or ensure Close is the last operation (e.g. via a single owner goroutine).
- Use errors.Is(err, flate's closed sentinel) where exposed, or check the closed flag you own, to distinguish this from a real I/O error.
Example fix
// before w, _ := flate.NewWriter(buf, flate.DefaultCompression) w.Write(data) w.Close() w.Write(more) // -> flate: closed writer // after: reuse via Reset for the next stream w, _ := flate.NewWriter(buf, flate.DefaultCompression) w.Write(data) w.Close() w.Reset(newBuf) // re-initialize onto a fresh writer w.Write(more) w.Close()
Defensive patterns
Strategy: validation
Validate before calling
// Guard against writing to a closed flate.Writer.
type safeFlate struct {
w *flate.Writer
closed bool
}
func (s *safeFlate) Write(p []byte) (int, error) {
if s.closed { return 0, errors.New("flate: closed writer") }
return s.w.Write(p)
}
func (s *safeFlate) Close() error {
if s.closed { return nil }
s.closed = true
return s.w.Close()
} Type guard
// flate does not export the sentinel; match by string or keep your own closed flag.
func isClosedWriter(err error) bool {
return err != nil && err.Error() == "flate: closed writer"
} Try / catch
if _, err := w.Write(p); err != nil {
if err.Error() == "flate: closed writer" {
// reopen via Reset or a new Writer and retry once
}
} Prevention
- Treat Close as terminal; never write afterward.
- Use Reset to reuse a compressor for a new stream rather than reusing a closed one.
- Single-owner the Writer so no goroutine writes post-close.
When it happens
Trigger: Calling Write (or Flush) on a *flate.Writer / *flate.Compressor after Close has succeeded. compressor.write and syncFlush both check `d.err != nil` at the top and short-circuit returning d.err, which is errWriterClosed.
Common situations: A defer that writes after the explicit Close; a goroutine that does not observe the close; wrapping a flate.Writer in another writer whose Close does not prevent further writes; misuse of Reset on a closed writer without re-initializing. Common in streaming/HTTP handlers that close the response then continue writing.
Related errors
- flate: invalid compression level %d: want value in range [-2
- ErrWriteTooLong
- ErrFieldTooLong
- ErrWriteAfterClose
- errMissData
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/23ef338c916ca7e2.
Report an issue: GitHub.