Billionmail/BillionMail · error
invalid data length
Error message
invalid data length
What it means
Decrypt in the maillog_stat package base64-url-decodes an encrypted blob and requires at least 32 bytes (16-byte key + 16-byte IV material). If the decoded data is shorter than 32 bytes it returns 'invalid data length' without attempting AES decryption, indicating the ciphertext is truncated or was never produced by the matching Encrypt function.
Source
Thrown at core/internal/service/maillog_stat/encryption.go:55
result := base64.URLEncoding.EncodeToString(resultBytes)
return strings.TrimRight(result, "=")
}
func Decrypt(data string, result interface{}) (err error) {
dataLength := len(data)
amountToPad := 4 - (dataLength % 4)
if amountToPad > 0 && amountToPad < 4 {
data += strings.Repeat("=", amountToPad)
}
dataAes, err := base64.URLEncoding.DecodeString(data)
if err != nil {
return
}
if len(dataAes) < 32 {
err = fmt.Errorf("invalid data length")
return
}
dataAesLen := len(dataAes)
keyiv := make([]byte, 0, 32)
keyiv = append(keyiv, dataAes[:16]...)
keyiv = append(keyiv, dataAes[dataAesLen-16:]...)
var key, iv []byte
for i := 0; i < 32; i++ {
if i%2 == 0 {
key = append(key, keyiv[i])
} else {
iv = append(iv, keyiv[i])
}
}
block, err := aes.NewCipher(key)View on GitHub (pinned to fc36c76c05)
Solutions
- Confirm the value was produced by the matching Encrypt function in this package (key+IV+AES format)
- Validate the input is non-empty, valid base64 URL encoding, and decodes to >=32 bytes before calling Decrypt
- Check the DB column/data source for truncation (column length limits, manual edits)
- Re-encrypt the affected data since the original key material cannot be recovered from a truncated blob
Example fix
// before
plain, err := Decrypt(storedValue)
// after
raw, err := base64.URLEncoding.DecodeString(storedValue)
if err != nil || len(raw) < 32 {
// handle: value is missing/corrupt; re-encrypt and store anew
return fallbackValue
}
plain, err := Decrypt(storedValue) Defensive patterns
Strategy: type-guard
Validate before calling
func isDecryptable(data string) bool {
raw, err := base64.URLEncoding.DecodeString(data)
return err == nil && len(raw) >= 32
}
if !isDecryptable(stored) { /* re-encrypt or use fallback */ } Type guard
func validCiphertext(data string) bool {
raw, err := base64.URLEncoding.DecodeString(data)
return err == nil && len(raw) >= 32
} Try / catch
plain, err := Decrypt(stored)
if err != nil {
if err.Error() == "invalid data length" || errors.Is(err, base64.CorruptInputError(0)) {
// treat as lost data: log, re-encrypt source, return zero value
return zeroValue, nil
}
return nil, err
} Prevention
- Always round-trip through Encrypt/Decrypt in tests for stored values
- Check DB column lengths don't truncate base64 blobs
- Never store plaintext or third-party-encoded values in fields Decrypt reads
- Validate base64-decoded length >= 32 before decrypting legacy data
When it happens
Trigger: Decrypt(data) is called with a string that decodes to fewer than 32 bytes — e.g. an empty string, invalid base64 that decoded to partial garbage, a manually truncated value, or a value encrypted/shortened by a different scheme.
Common situations: Stored database values corrupted or written by an older/other code path; passing plaintext instead of encrypted data; passing the base64 of a short value from tests; manual copy-paste truncation of tokens.
Related errors
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/f37b4cd01d320e3f.
Report an issue: GitHub.