{"record":{"id":"a9d82a98f8141e38","repo":"FiloSottile/age","slug":"failed-to-decrypt-and-authenticate-payload-chunk","errorCode":null,"errorMessage":"failed to decrypt and authenticate payload chunk, file may be corrupted or tampered with","messagePattern":"failed to decrypt and authenticate payload chunk, file may be corrupted or tampered with","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/stream/stream.go","lineNumber":150,"sourceCode":"\t\t\treturn false, errors.New(\"last chunk is empty, try age v1.0.0, and please consider reporting this\")\n\t\t}\n\t\tin = in[:n]\n\t\tlast = true\n\t\tsetLastChunkFlag(&r.nonce)\n\tcase err != nil:\n\t\treturn false, err\n\t}\n\n\toutBuf := make([]byte, 0, ChunkSize)\n\tout, err := r.a.Open(outBuf, r.nonce[:], in, nil)\n\tif err != nil && !last {\n\t\t// Check if this was a full-length final chunk.\n\t\tlast = true\n\t\tsetLastChunkFlag(&r.nonce)\n\t\tout, err = r.a.Open(outBuf, r.nonce[:], in, nil)\n\t}\n\tif err != nil {\n\t\treturn false, errors.New(\"failed to decrypt and authenticate payload chunk, file may be corrupted or tampered with\")\n\t}\n\n\tincNonce(&r.nonce)\n\tr.unread = r.buf[:copy(r.buf[:], out)]\n\treturn last, nil\n}\n\nfunc incNonce(nonce *[chacha20poly1305.NonceSize]byte) {\n\tfor i := len(nonce) - 2; i >= 0; i-- {\n\t\tnonce[i]++\n\t\tif nonce[i] != 0 {\n\t\t\treturn\n\t\t}\n\t}\n\t// The counter is 88 bits, this is unreachable.\n\tpanic(\"stream: chunk counter wrapped around\")\n}\n","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/internal/stream/stream.go#L132-L168","documentation":"The ChaCha20Poly1305 AEAD Open call failed on a payload chunk, meaning authentication (MAC) or decryption failed. Since the header MAC already passed, this means the encrypted payload was modified, corrupted, or encrypted with a different file key. The reader treats any single bad chunk as a fatal error for the whole file.","triggerScenarios":"stream.Reader.readChunk calls r.a.Open and it returns a non-nil error — wrong identity/key (mismatched file key), bit rot, truncated bit flips, or tampered ciphertext bytes within any chunk.","commonSituations":"Decrypting with the wrong identity (file encrypted for someone else); corrupted downloads or damaged storage; malicious modification of the .age file; copying the file in text mode that altered bytes.","solutions":["Confirm you are using the correct identity/private key that the file was encrypted for.","Re-download or re-copy the file and verify its checksum against the sender.","Re-encrypt from the original plaintext if the source is corrupted beyond repair.","Check that no transport step (FTP ascii mode, editor save) mangled the binary ciphertext."],"exampleFix":"// before\nr, _ := age.Decrypt(input, wrongIdentity) // payload chunks fail auth\n// after\nr, err := age.Decrypt(input, correctIdentity)\nif err != nil { return fmt.Errorf(\"decrypt: %w\", err) }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if _, err := io.Copy(out, r); err != nil {\n    if strings.Contains(err.Error(), \"failed to decrypt and authenticate payload chunk\") {\n        return errors.New(\"wrong key or corrupted/tampered file\")\n    }\n    return err\n}","preventionTips":["Verify you hold the identity the file was encrypted for before long processing.","Use binary-safe transfer (no text-mode FTP, no editor round-trips) for .age files.","Distribute checksums alongside ciphertext and verify before decrypting."],"tags":["encryption","aead","integrity"],"backgroundTag":"payload-decrypt-auth-failed","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}