golang/go · error
ErrWriteAfterClose
ErrWriteAfterClose
Error message
archive/tar: write after close
What it means
ErrWriteAfterClose is returned by Write, WriteHeader, Flush, and ReadFrom after Close has been called on the Writer. Close sets an internal error sentinel (tw.err = ErrWriteAfterClose) that all subsequent operations check, so any post-close use is rejected. Close itself is idempotent (returns nil if already closed).
Source
Thrown at src/archive/tar/common.go:37
"math"
"path"
"reflect"
"strconv"
"strings"
"time"
)
// BUG: Use of the Uid and Gid fields in Header could overflow on 32-bit
// architectures. If a large value is encountered when decoding, the result
// stored in Header will be the truncated version.
var tarinsecurepath = godebug.New("tarinsecurepath")
var (
ErrHeader = errors.New("archive/tar: invalid tar header")
ErrWriteTooLong = errors.New("archive/tar: write too long")
ErrFieldTooLong = errors.New("archive/tar: header field too long")
ErrWriteAfterClose = errors.New("archive/tar: write after close")
ErrInsecurePath = errors.New("archive/tar: insecure file path")
errMissData = errors.New("archive/tar: sparse file references non-existent data")
errUnrefData = errors.New("archive/tar: sparse file contains unreferenced data")
errWriteHole = errors.New("archive/tar: write non-NUL byte in sparse hole")
errSparseTooLong = errors.New("archive/tar: sparse map too long")
)
type headerError []string
func (he headerError) Error() string {
const prefix = "archive/tar: cannot encode header"
var ss []string
for _, s := range he {
if s != "" {
ss = append(ss, s)
}
}
if len(ss) == 0 {View on GitHub (pinned to b6b368adc5)
Solutions
- Treat Close as terminal: ensure no Write/WriteHeader/Flush calls happen after it (use a sync.Once or a closed flag).
- Do not defer Close alongside concurrent writes; serialize close with a mutex or channel.
- In error paths, return immediately after Close instead of continuing the write loop.
- If pooling writers, always create a fresh tar.NewWriter; never reuse a closed one.
Example fix
// before
tw := tar.NewWriter(w)
tw.Close()
tw.Write([]byte("x")) // throws ErrWriteAfterClose
// after
tw := tar.NewWriter(w)
defer tw.Close()
// ... all writes happen here, before defer runs Defensive patterns
Strategy: try-catch
Validate before calling
type safeWriter struct{ *tar.Writer; closed bool }
func (s *safeWriter) Close() error { s.closed = true; return s.Writer.Close() }
func (s *safeWriter) Write(b []byte) (int, error) {
if s.closed { return 0, nil } // or return tar.ErrWriteAfterClose
return s.Writer.Write(b)
} Try / catch
_, err := tw.Write(b)
if errors.Is(err, tar.ErrWriteAfterClose) {
return fmt.Errorf("logic error: write after close on %s", name)
} Prevention
- Treat Close as terminal; never write after it.
- Serialize close with a mutex when writers are shared across goroutines.
- Return immediately from error paths that close the writer.
When it happens
Trigger: Calling tw.Write(...) or tw.WriteHeader(...) after tw.Close(); flushing or copying into the writer after finalizing the footer; reused Writer instance whose Close was triggered early by an error in a previous operation.
Common situations: Deferred Close racing with an explicit write in a goroutine; error handling that calls Close on a partial write then continues the loop; cleanup paths that close prematurely; misusing a pooled/tar writer that was returned to the pool closed.
Related errors
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/d69e7fdee15e5592.
Report an issue: GitHub.