kopia/kopia · error
error hashing
Error message
error hashing
What it means
VerifyAndStrip validates an HMAC-SHA256 signature appended to the end of an input byte range and, while doing so, streams the payload into both the HMAC and the output writer via io.CopyN. This error wraps any failure of that copy — typically the input is SHORTER than expected (fewer than p bytes available), the underlying reader fails, or the output writer fails.
Solutions
- Check for the wrapped cause: an io.ErrUnexpectedEOF/EOF usually means the input is truncated — delete the corrupted cache entry and re-fetch
- Verify input.Length() is accurate and the signature suffix (last sha256.Size bytes) is present before calling VerifyAndStrip
- Re-download or re-verify the blob from the primary storage if the cache copy is truncated
- If the output writer is the failure, ensure it is open and writable before verification
Example fix
// before
if _, err := io.CopyN(io.MultiWriter(h, output), r, int64(p)); err != nil {
return errors.Wrap(err, "error hashing")
}
// after
if _, err := io.CopyN(io.MultiWriter(h, output), r, int64(p)); err != nil {
return errors.Wrapf(err, "error hashing %d-byte payload (input length %d)", p, input.Length())
} Defensive patterns
Strategy: validation
Validate before calling
// verify the input plausibly contains payload + signature before verifying
if input.Length() < sha256.Size {
return errors.New("input too short to contain HMAC signature")
} Try / catch
if err := h.VerifyAndStrip(input, output, secret); err != nil {
if stderrors.Is(err, io.ErrUnexpectedEOF) || stderrors.Is(err, io.EOF) {
// treat as corrupted/truncated cache entry: invalidate and re-fetch
}
return fmt.Errorf("verification failed: %w", err)
} Prevention
- Validate cached blob lengths match metadata before verification
- Delete and re-fetch cache entries on any read/verification error
- Avoid mutating byte ranges concurrently with VerifyAndStrip
- Detect signature suffix presence (last 32 bytes) before attempting verification
When it happens
Trigger: Calling Verify (or reading blobs from cache) where input.Length() - sha256.Size exceeds the actual readable bytes, the underlying storage reader errors mid-copy, or the destination output writer fails while draining the payload.
Common situations: Truncated or corrupted cache files whose declared length exceeds real content; storage backend read errors during verification; concurrent modification shrinking the data between Length() and read.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- ErrInvalidOffset
- error appending
- error closing index file
- error reading chunk
- error reading placeholder for
AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07).
Data as JSON: /api/errors/14341fc2144e2bdb.
Report an issue: GitHub.
Appendix: source
Thrown at internal/hmac/hmac.go:38
var hash [sha256.Size]byte
output.Write(h.Sum(hash[:0])) //nolint:errcheck
}
// VerifyAndStrip verifies that given block of bytes has correct HMAC-SHA256 checksum and strips it.
func VerifyAndStrip(input gather.Bytes, secret []byte, output *gather.WriteBuffer) error {
if input.Length() < sha256.Size {
return errors.New("invalid data - too short")
}
p := input.Length() - sha256.Size
h := hmac.New(sha256.New, secret)
r := input.Reader()
if _, err := io.CopyN(io.MultiWriter(h, output), r, int64(p)); err != nil {
return errors.Wrap(err, "error hashing")
}
var sigBuf, actualSignature [sha256.Size]byte
validSignature := h.Sum(sigBuf[:0])
n, err := r.Read(actualSignature[:])
if err != nil || n != sha256.Size {
return errors.Wrap(err, "error reading signature")
}
if hmac.Equal(validSignature, actualSignature[:]) {
return nil
}
return errors.New("invalid data - corrupted")
}
View on GitHub (pinned to 82495e54b5)