restic/restic · error
decompressing blob %v from pack %v failed: %w
Error message
decompressing blob %v from pack %v failed: %w
What it means
zstd DecodeAll failed on a blob whose header entry has the compression flag set, so after successful decryption the payload is not valid zstd data. This means the compressed plaintext is damaged (bitrot that still decrypts is rare because MAC failure would fire first, so typically the entry flags and the stored bytes disagree) or the blob was written uncompressed while flagged compressed. Thrown from packBlobIterator.Next during any pack-reading operation.
Source
Thrown at internal/repository/repository.go:1334
if int(entry.Length) <= b.key.NonceSize() {
debug.Log("%v", b.blobs)
return packBlobValue{}, fmt.Errorf("invalid blob length %v", entry)
}
// decryption errors are likely permanent, give the caller a chance to skip them
nonce, ciphertext := buf[:b.key.NonceSize()], buf[b.key.NonceSize():]
plaintext, err := b.key.Open(ciphertext[:0], nonce, ciphertext, nil)
if err != nil {
err = fmt.Errorf("decrypting blob %v from pack %v failed: %w", h, b.packID.String(), err)
}
if err == nil && entry.IsCompressed() {
// DecodeAll will allocate a slice if it is not large enough since it
// knows the decompressed size (because we're using EncodeAll)
b.decode, err = b.dec.DecodeAll(plaintext, b.decode[:0])
plaintext = b.decode
if err != nil {
err = fmt.Errorf("decompressing blob %v from pack %v failed: %w", h, b.packID.String(), err)
}
}
if err == nil {
id := restic.Hash(plaintext)
if !id.Equal(entry.ID) {
debug.Log("read blob %v/%v from pack %v: wrong data returned, hash is %v",
h.Type, h.ID, b.packID.String(), id)
err = fmt.Errorf("read blob %v from pack %v: wrong data returned, hash is %v",
h, b.packID.String(), id)
}
}
return packBlobValue{entry.BlobHandle, plaintext, err}, nil
}
func (r *Repository) zeroChunk() restic.ID {
r.zeroChunkOnce.Do(func() {
r.zeroChunkID = restic.Hash(make([]byte, chunker.MinSize))View on GitHub (pinned to a80be1478a)
Solutions
- Run restic check --read-data to identify the affected packs
- Rebuild the index (restic rebuild-index) so entry metadata matches actual pack headers
- Recover the pack from a second copy or backend versioning
- Re-backup affected data and prune the damaged pack
Defensive patterns
Strategy: try-catch
Try / catch
if err != nil && strings.Contains(err.Error(), "decompressing blob") {
// entry metadata disagrees with stored bytes; rebuild index and re-verify
log.Printf("pack %s: blob %v fails zstd decode — run restic rebuild-index then restic check", packID, h.ID.Str())
return nil // skip in scan mode
} Prevention
- Do not mix restic versions from the compression-feature development window on one repo
- Verify repository integrity (restic check) before and after migrations or major version upgrades
- Treat index rebuilds on unverified packs as suspect; prefer check first
When it happens
Trigger: A pack header entry flagged IsCompressed whose blob bytes are raw/uncompressed (mixed-version writer bug, manual pack surgery); corruption of the blob body in a way that preserves authentication is not possible under the MAC, so in practice the entry metadata is the corrupted part; reading v2-compressed packs with a mismatched index.
Common situations: Repositories touched by prerelease or mismatched restic versions during the compression feature rollout; indexes rebuilt incorrectly so blob lengths/flags point into the middle of another blob.
Related errors
- overlapping blobs in pack %v
- readFull: %w
- invalid blob length %v
- decrypting blob %v from pack %v failed: %w
- read blob %v from pack %v: wrong data returned, hash is %v
AI-assisted analysis of restic/restic@a80be1478a (2026-08-15).
Data as JSON: /api/errors/beb1b155307d6b00.
Report an issue: GitHub.