restic/restic · critical

header decoding failed: %w

Error message

header decoding failed: %w

What it means

The self-check performed right after encrypting a pack header failed at its first step: List() could not decode the header bytes that were just encrypted. A freshly encrypted header is by construction valid, so a decode failure practically means the bytes changed in memory between Seal and verify - the symptom restic associates with hardware faults or software bugs.

Source

Thrown at internal/repository/pack/pack.go:138

		return p.err
	}

	if n != len(encryptedHeader) {
		p.err = errors.New("wrong number of bytes written")
		return p.err
	}
	p.bytes += uint(len(encryptedHeader))

	return nil
}

func verifyHeader(k *crypto.Key, header []byte, expected []Blob) error {
	// do not offer a way to skip the pack header verification, as pack headers are usually small enough
	// to not result in a significant performance impact

	decoded, hdrSize, err := List(k, bytes.NewReader(header), int64(len(header)))
	if err != nil {
		return fmt.Errorf("header decoding failed: %w", err)
	}
	if hdrSize != uint32(len(header)) {
		return fmt.Errorf("unexpected header size %v instead of %v", hdrSize, len(header))
	}
	if len(decoded) != len(expected) {
		return fmt.Errorf("pack header size mismatch")
	}
	for i := range decoded {
		if decoded[i] != expected[i] {
			return fmt.Errorf("pack header entry mismatch got %v instead of %v", decoded[i], expected[i])
		}
	}
	return nil
}

// HeaderOverhead returns an estimate of the number of bytes written by a call to Finalize.
func (p *Packer) HeaderOverhead() int {
	return crypto.CiphertextLength(0) + binary.Size(uint32(0))

View on GitHub (pinned to a80be1478a)

Solutions

  1. Run a memory test (memtest86+) and verify system stability under load
  2. Retry the backup; if it fails again on the same data, suspect hardware
  3. Update to the latest restic release before further debugging
  4. If reproducible on stable hardware, report it with the wrapped error and debug logs
Defensive patterns

Strategy: try-catch

Type guard

func isHeaderDecodeFailure(err error) bool {
    return err != nil && strings.Contains(err.Error(), "header decoding failed")
}

Try / catch

if err := verifyHeader(k, encryptedHeader, blobs); err != nil {
    if isHeaderDecodeFailure(err) {
        // freshly encrypted data failed to decode: abort and collect hardware diagnostics
        return abortWithDiagnostics(err)
    }
    return err
}

Prevention

When it happens

Trigger: Bit flips in RAM or CPU caches during the finalize path; failing storage for temporary buffers; a bug in the crypto or packer layer of that restic version.

Common situations: Large backups on unstable or overclocked machines; recurring finalize failures on one host; virtualization memory issues.

Related errors


AI-assisted analysis of restic/restic@a80be1478a (2026-08-15). Data as JSON: /api/errors/9f3797a1bccb4e0e. Report an issue: GitHub.