{"record":{"id":"4ef4c5fc65f77f49","repo":"FiloSottile/age","slug":"invalid-encrypted-payload-size-d","errorCode":null,"errorMessage":"invalid encrypted payload size: %d","messagePattern":"invalid encrypted payload size: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/stream/stream.go","lineNumber":25,"sourceCode":"\nimport (\n\t\"bytes\"\n\t\"crypto/cipher\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"sync/atomic\"\n\n\t\"golang.org/x/crypto/chacha20poly1305\"\n)\n\nconst ChunkSize = 64 * 1024\n\nfunc EncryptedChunkCount(encryptedSize int64) (int64, error) {\n\tif encryptedSize < 0 || encryptedSize > math.MaxInt64-encChunkSize+1 {\n\t\treturn 0, fmt.Errorf(\"invalid encrypted payload size: %d\", encryptedSize)\n\t}\n\tchunks := (encryptedSize + encChunkSize - 1) / encChunkSize\n\n\tplaintextSize := encryptedSize - chunks*chacha20poly1305.Overhead\n\texpChunks := (plaintextSize + ChunkSize - 1) / ChunkSize\n\t// Empty plaintext, the only case that allows (and requires) an empty chunk.\n\tif plaintextSize == 0 {\n\t\texpChunks = 1\n\t}\n\tif expChunks != chunks {\n\t\treturn 0, fmt.Errorf(\"invalid encrypted payload size: %d\", encryptedSize)\n\t}\n\n\treturn chunks, nil\n}\n\nfunc PlaintextSize(encryptedSize int64) (int64, error) {\n\tchunks, err := EncryptedChunkCount(encryptedSize)","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/internal/stream/stream.go#L7-L43","documentation":"EncryptedChunkCount validates the size of an encrypted STREAM payload. This error means the caller passed a size that cannot possibly be a valid encrypted payload: either a negative number, or a size so large the chunk count arithmetic would overflow. The library refuses to guess a chunk count from malformed sizes because the chunk structure determines the plaintext layout.","triggerScenarios":"Calling stream.EncryptedChunkCount(encryptedSize) with encryptedSize < 0 or with encryptedSize > math.MaxInt64 - encChunkSize + 1. Also surfaces via PlaintextSize and NewDecryptReaderAt, which call EncryptedChunkCount internally.","commonSituations":"Passing a plaintext file size instead of the ciphertext size; passing an os.Stat size from a truncated or still-being-written file; integer misuse where -1 is used as a sentinel for 'unknown size'.","solutions":["Verify the size you pass is the ciphertext (encrypted) size, not the plaintext size; ciphertext = plaintext + 16 bytes per 64 KiB chunk.","Check the value is non-negative and comes from a reliable source such as os.File.Stat().Size() after the write completed.","If the file may be truncated or incomplete, wait for the writer to finish or re-stat the file before computing sizes.","If a sentinel negative value is in play, handle 'unknown size' explicitly before calling the API."],"exampleFix":"// before\nn, err := stream.EncryptedChunkCount(-1) // sentinel for unknown\n// after\nif size < 0 {\n    return 0, errors.New(\"encrypted size unknown; stat the file first\")\n}\nn, err := stream.EncryptedChunkCount(size)","handlingStrategy":"validation","validationCode":"const encChunkSize = 64*1024 + 16\nfunc validEncryptedSize(size int64) bool {\n    return size >= 0 && size <= math.MaxInt64-encChunkSize+1\n}\n// call EncryptedChunkCount only if validEncryptedSize(size)","typeGuard":null,"tryCatchPattern":"chunks, err := stream.EncryptedChunkCount(size)\nif err != nil {\n    return fmt.Errorf(\"cannot determine chunk layout for size %d: %w\", size, err)\n}","preventionTips":["Always pass ciphertext size (Stat().Size()), never plaintext size or sentinels like -1.","Reject negative and 'unknown' sizes before calling into the stream package.","Don't hand-compute chunk counts; let the library derive them."],"tags":["stream","validation","size-mismatch","go"],"backgroundTag":"invalid-encrypted-payload-size","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}