Billionmail/BillionMail · error
base64 decode failed: %w
Error message
base64 decode failed: %w
What it means
After successful hex decoding, the bytes are expected to be base64-encoded plaintext. base64.StdEncoding.DecodeString fails on characters outside the base64 alphabet, wrong padding, or invalid length, yielding 'base64 decode failed' with the underlying error wrapped via %w.
Source
Thrown at core/internal/service/mail_service/sending.go:168
return
}
// PasswordPlainByEmail
func PasswordPlainByEmail(ctx context.Context, email string) (string, error) {
val, err := g.DB().Model("mailbox").Where("username", email).Value("password_encode")
if err != nil {
return "", fmt.Errorf("query password failed: %w", err)
}
if val.IsEmpty() {
return "", fmt.Errorf("password not found for %s", email)
}
rawHex, err := hex.DecodeString(val.String())
if err != nil {
return "", fmt.Errorf("hex decode failed: %w", err)
}
plainBytes, err := base64.StdEncoding.DecodeString(string(rawHex))
if err != nil {
return "", fmt.Errorf("base64 decode failed: %w", err)
}
return string(plainBytes), nil
}
// Close closes the SMTP connection
func (e *EmailSender) Close() {
_ = e.Disconnect()
}
// Connect establishes an SMTP connection
func (e *EmailSender) Connect() error {
e.mutex.Lock()
defer e.mutex.Unlock()
if e.connected && e.client != nil {
// Connection already exists
return nil
}View on GitHub (pinned to fc36c76c05)
Solutions
- Decode the hex payload and check whether it already looks like plaintext — if so, the base64 step was skipped when storing; re-store as hex(base64(pw)).
- Reset the mailbox password through the application so it is encoded with the current scheme.
- Ensure any import/migration script applies hex(base64(password)), matching the code path in PasswordPlainByEmail.
- Inspect padding/length of the base64 payload; malformed padding causes DecodeString failure.
Example fix
// before enc := hex.EncodeToString([]byte(password)) // wrong: single encoding // after b64 := base64.StdEncoding.EncodeToString([]byte(password)) enc := hex.EncodeToString([]byte(b64))
Defensive patterns
Strategy: validation
Validate before calling
v, err := g.DB().Model("mailbox").Where("username", email).Value("password_encode")
if err == nil && !v.IsEmpty() {
raw, _ := hex.DecodeString(v.String())
if _, berr := base64.StdEncoding.DecodeString(string(raw)); berr != nil {
return fmt.Errorf("password_encode for %s is hex but not base64; re-store as hex(base64(pw))", email)
}
} Type guard
func isHexOfBase64(s string) bool {
raw, err := hex.DecodeString(s)
if err != nil {
return false
}
_, err = base64.StdEncoding.DecodeString(string(raw))
return err == nil
} Try / catch
pass, err := PasswordPlainByEmail(ctx, email)
if err != nil {
var b64Err base64.CorruptInputError
if errors.As(err, &b64Err) {
return fmt.Errorf("mailbox %s password mis-encoded (missing base64 layer); reset password", email)
}
return err
} Prevention
- Apply the full hex(base64(password)) encoding in any migration/import script.
- Add a round-trip test that encodes then decodes a sample password.
- Reset passwords that predate an encoding-scheme change.
When it happens
Trigger: PasswordPlainByEmail decodes a value that was hex-valid but whose payload is not base64 — e.g. the column stores hex(plaintext) instead of hex(base64(plaintext)), or padding was lost.
Common situations: Double-encoding mistake when seeding data manually; migration script encoded only once instead of twice; value written by a different tool with a different scheme.
Related errors
- hex decode failed: %w
- password length must be at least 4 characters
- Generate password md5-crypt failed: %w
- Decode password failed: %w
- query password failed: %w
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/0318bd37bb33bd98.
Report an issue: GitHub.