golang/go · error

errWriteHole

errWriteHole

Error message

archive/tar: write non-NUL byte in sparse hole

What it means

errWriteHole (unexported) is returned by the writer when a non-NUL byte is written into a sparse hole region. Sparse holes are stored as zero gaps; writing real data there is a logic error because the hole has no allocated dense bytes. The zeroWriter only accepts NUL (0x00) bytes and rejects anything else.

Source

Thrown at src/archive/tar/common.go:41

	"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 {
		return prefix
	}
	return fmt.Sprintf("%s: %v", prefix, strings.Join(ss, "; and "))
}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Align writes with the sparse map: write only into declared data fragments, and write NUL bytes (or nothing) into holes.
  2. When you have a fully materialized file, do not declare sparse holes at all — use a normal regular file header.
  3. Pre-zero any buffer used for padding: bytes.Fill(buf, 0) before writing it into hole regions.
  4. If you genuinely need to write data where a hole was, fix the Sparses map first so the region becomes a data fragment.

Example fix

// before
hdr := &tar.Header{Size: 10, Sparses: []tar.SparseEntry{{Offset: 5, NumBytes: 5}}}
tw.WriteHeader(hdr)
tw.Write([]byte("abcdefghij")) // bytes 0-4 are a hole, throws errWriteHole

// after
hdr := &tar.Header{Size: 10, Sparses: []tar.SparseEntry{{Offset: 5, NumBytes: 5}}}
tw.WriteHeader(hdr)
tw.Write(bytes.Repeat([]byte{0}, 5)) // hole region, NUL ok
tw.Write([]byte("fghij"))           // data fragment
Defensive patterns

Strategy: validation

Validate before calling

// Before writing, ensure your buffer offsets align with sparse data fragments,
// and that any hole-region bytes are zero.
for _, b := range buf {
  if b != 0 { /* writing into a hole: re-check sparse map */ }
}

Type guard

func isAllZero(b []byte) bool { for _, x := range b { if x != 0 { return false } }; return true }

Prevention

When it happens

Trigger: Calling Write on a tar entry that has sparse holes, and pushing a buffer that overlaps a hole region with non-zero bytes; mis-sequencing writes so data lands at an offset the sparse map designates as a hole.

Common situations: Manually writing a sparse file without aligning writes to the declared fragment offsets; buggy code that interleaves data and padding without zeroing the padding; using io.Copy into a sparse-aware writer when the source contains unexpected non-zero bytes in what should be holes.

Related errors


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