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

  1. 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)).
  2. Reset the mailbox password through the application so it is encoded with the current scheme.
  3. Ensure any import/migration script applies hex(base64(password)), matching the code path in PasswordPlainByEmail.
  4. 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

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


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/0318bd37bb33bd98. Report an issue: GitHub.