kopia/kopia · error

unable to write header

Error message

unable to write header

What it means

This error wraps a failed Write of the v2 index header (a 17+ byte binary header) to the underlying writer w during index Finish. The header includes version, key length, entry count, pack ID count, format-info count and base timestamp; if writing those bytes fails, the error is wrapped with "unable to write header" so the failure point is identifiable. The root cause is whatever the underlying writer reported.

Solutions

  1. Inspect the wrapped cause with %+v to see the underlying write error (ENOSPC, EPIPE, file closed, etc.).
  2. Check available disk space and quotas on the destination volume; free space or redirect the index output.
  3. Ensure the writer is valid and open for the whole duration of the Finish call.
  4. Retry the index build after fixing the storage condition; the index was not fully written.
Defensive patterns

Strategy: try-catch

Validate before calling

// check writable space before building:
if stat, err := outDirStat(); err != nil || stat.freeBytes() < estimatedIndexSize {
    return errors.New("insufficient space for index output")
}

Try / catch

if err := idxBuilder.Finish(w); err != nil && strings.Contains(err.Error(), "unable to write header") {
    log.Errorf("index header write failed: %+v", err) // inspect wrapped cause
}

Prevention

When it happens

Trigger: Any io.Writer error returned by w.Write(header) in the v2 index Finish path — disk full, closed writer, broken pipe to a pipe/network sink, or permission issues on the output file.

Common situations: Running out of disk space while committing an index, writing to an already-closed buffer, or streaming the index to a destination that disappeared mid-write.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07). Data as JSON: /api/errors/ba02ec7762ea9b32. Report an issue: GitHub.

Appendix: source

Thrown at repo/content/index/index_v2.go:525

	}

	uniqueFormatInfo2IndexLen := len(b2.uniqueFormatInfo2Index)
	if uniqueFormatInfo2IndexLen > maxUInt8 {
		return errors.Errorf("invalid unique format v2 info index length: %v", uniqueFormatInfo2IndexLen)
	}

	// write header
	header := make([]byte, v2IndexHeaderSize)
	header[0] = Version2                                                   // version
	header[1] = byte(b2.keyLength)                                         //nolint:gosec // range checked above
	binary.BigEndian.PutUint16(header[2:4], uint16(b2.entrySize))          //nolint:gosec
	binary.BigEndian.PutUint32(header[4:8], uint32(b2.entryCount))         //nolint:gosec
	binary.BigEndian.PutUint32(header[8:12], uint32(len(b2.packID2Index))) //nolint:gosec
	header[12] = byte(uniqueFormatInfo2IndexLen)
	binary.BigEndian.PutUint32(header[13:17], uint32(b2.baseTimestamp)) //nolint:gosec

	if _, err := w.Write(header); err != nil {
		return errors.Wrap(err, "unable to write header")
	}

	// write sorted index entries
	for _, it := range sortedInfos {
		if err := b2.writeIndexEntry(w, it); err != nil {
			return errors.Wrap(err, "unable to write entry")
		}
	}

	// write pack ID entries in the index order of values from packID2Index (0, 1, 2, ...).
	reversePackIDIndex := make([]blob.ID, len(b2.packID2Index))
	for k, v := range b2.packID2Index {
		reversePackIDIndex[v] = k
	}

	// emit pack ID information in this order.
	for _, e := range reversePackIDIndex {
		if err := b2.writePackIDEntry(w, e); err != nil {

View on GitHub (pinned to 82495e54b5)