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

  1. Stop writing to the Writer after Close; treat Close as terminal for that instance.
  2. If you need to compress again, create a new Writer (or use Reset to re-initialize a reusable compressor with a fresh underlying writer).
  3. Guard writes with a closed flag or ensure Close is the last operation (e.g. via a single owner goroutine).
  4. 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

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


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/23ef338c916ca7e2. Report an issue: GitHub.