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

  1. Confirm you are using the correct password and repository URL for this repo
  2. Run restic check --read-data to confirm and scope pack-level damage
  3. Check hardware: SMART for disks, memtest for RAM, when multiple packs fail to decrypt
  4. 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

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


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