restic/restic · error
decrypting blob %v from pack %v failed: %w
Error message
decrypting blob %v from pack %v failed: %w
What it means
key.Open failed while decrypting a blob read from a pack: the ciphertext did not authenticate under the repository key. The code comments that decryption errors are 'likely permanent' and gives the caller a chance to skip them, because the usual causes are persistent: bitrot in pack data, wrong repository password/key, or a pack overwritten with different bytes. Emitted from packBlobIterator.Next and delivered to the handleBlobFn callback of LoadBlobsFromPack.
Source
Thrown at internal/repository/repository.go:1326
buf, err := b.rd.ReadFull(int(entry.Length))
if err != nil {
debug.Log(" read error %v", err)
return packBlobValue{}, fmt.Errorf("readFull: %w", err)
}
b.currentOffset = entry.Offset + entry.Length
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)
}View on GitHub (pinned to a80be1478a)
Solutions
- Confirm you are using the correct password and repository URL for this repo
- Run restic check --read-data to confirm and scope pack-level damage
- Check hardware: SMART for disks, memtest for RAM, when multiple packs fail to decrypt
- Recover the affected pack from a replica/second copy, or re-backup the source data
Defensive patterns
Strategy: try-catch
Try / catch
err := repo.LoadBlobsFromPack(ctx, packID, handles, func(h restic.BlobHandle, buf []byte, err error) error {
if err != nil {
if strings.Contains(err.Error(), "decrypting blob") {
log.Printf("permanent: blob %v fails authentication in pack %s — likely wrong key or damaged pack", h, packID)
}
return err // caller may skip: return nil after logging in scan scenarios
}
return nil
}) Prevention
- Store the repo password in a managed secret and record which password belongs to which repo
- Verify the password right after repository creation and after credential restores (restic snapshots is a cheap smoke test)
- Keep restic copy replicas so damaged packs have a fallback
When it happens
Trigger: Reading a blob (restore, dump, prune repack, verify) from a pack whose bytes were corrupted after upload; opening a repository with the wrong password so the derived key cannot authenticate any blob; two restic instances writing the same pack ID with different keys/content on a non-atomic backend.
Common situations: Wrong password after a restore of credentials or a keyfile mix-up between repos; failing disks/RAM corrupting objects in a local or SFTP repo; storage services that mangle objects (rare but seen with buggy gateways).
Related errors
- ErrUnauthenticated
- overlapping blobs in pack %v
- readFull: %w
- invalid blob length %v
- decompressing blob %v from pack %v failed: %w
AI-assisted analysis of restic/restic@a80be1478a (2026-08-15).
Data as JSON: /api/errors/8942309dac9a1339.
Report an issue: GitHub.