{"record":{"id":"5ae234f6004c46c4","repo":"kopia/kopia","slug":"invalid-data-corrupted","errorCode":null,"errorMessage":"invalid data - corrupted","messagePattern":"invalid data - corrupted","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/hmac/hmac.go","lineNumber":54,"sourceCode":"\n\tif _, err := io.CopyN(io.MultiWriter(h, output), r, int64(p)); err != nil {\n\t\treturn errors.Wrap(err, \"error hashing\")\n\t}\n\n\tvar sigBuf, actualSignature [sha256.Size]byte\n\n\tvalidSignature := h.Sum(sigBuf[:0])\n\n\tn, err := r.Read(actualSignature[:])\n\tif err != nil || n != sha256.Size {\n\t\treturn errors.Wrap(err, \"error reading signature\")\n\t}\n\n\tif hmac.Equal(validSignature, actualSignature[:]) {\n\t\treturn nil\n\t}\n\n\treturn errors.New(\"invalid data - corrupted\")\n}\n","sourceCodeStart":36,"sourceCodeEnd":56,"githubUrl":"https://github.com/kopia/kopia/blob/82495e54b584c1ef6073c9e1be048f57f8aef078/internal/hmac/hmac.go#L36-L56","documentation":"After recomputing the HMAC-SHA256 over the payload, VerifyAndStrip compares it with the trailing signature using hmac.Equal. When the two differ, the data fails authentication and the error 'invalid data - corrupted' is returned; the payload is never emitted to the output buffer.","triggerScenarios":"Calling VerifyAndStrip on blob data whose content or trailing 32-byte signature was modified — bit rot, truncated-then-overwritten cache files, wrong secret used for verification, or mixing data written with a different key.","commonSituations":"Failing disk or flaky storage backend corrupting cached blobs; rotating/incorrect HMAC secret in configuration; a cache directory shared between repositories with different secrets.","solutions":["Verify the configured HMAC secret matches the one used when the data was written.","Delete the corrupted cache/blob entry and re-fetch it from the source.","Check storage hardware/backend for corruption; run repository consistency checks."],"exampleFix":"// before\nerr := hmac.VerifyAndStrip(entry, secret, out) // fails: corrupted\n// after\nerr := hmac.VerifyAndStrip(entry, secret, out)\nif err != nil {\n    os.Remove(cachePath) // drop bad entry, refetch\n    entry = refetchBlob(blobID)\n    err = hmac.VerifyAndStrip(entry, secret, out)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := hmac.VerifyAndStrip(data, secret, out)\nif err != nil {\n    if strings.Contains(err.Error(), \"corrupted\") {\n        // authenticated failure: drop entry, refetch from canonical source\n        os.Remove(cachePath)\n        return refetchAndVerify(id)\n    }\n    return err\n}","preventionTips":["Never bypass or ignore VerifyAndStrip errors — the payload must not be used on failure.","Keep the HMAC secret in sync between writer and reader (single config source).","Enable storage-level checksums/scrubbing to detect bit rot early.","Never share a cache directory between repositories with different secrets."],"tags":["go","hmac","data-corruption","integrity"],"backgroundTag":"checksum-mismatch","analyzedSha":"82495e54b584c1ef6073c9e1be048f57f8aef078","analyzedAt":"2026-09-07T20:35:21.689Z","contentChangedAt":"2026-09-07T20:35:21.689Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}