Billionmail/BillionMail · warning

mailbox %s already exists

Error message

mailbox %s already exists

What it means

Add inserts the new mailbox row into the 'mailbox' table; if the DB rejects the insert with a duplicate/unique constraint violation, it is translated into this friendly 'mailbox %s already exists' error instead of surfacing the raw SQL error.

Source

Thrown at core/internal/service/mail_boxes/mail_boxes.go:49

	if err != nil {
		err = fmt.Errorf("Generate password md5-crypt failed: %w", err)
		return
	}

	mailbox.Username = strings.ToLower(mailbox.Username)
	mailbox.LocalPart = strings.ToLower(mailbox.LocalPart)
	mailbox.Domain = strings.ToLower(mailbox.Domain)

	now := time.Now().Unix()
	mailbox.CreateTime = now
	mailbox.UpdateTime = now
	mailbox.Active = 1
	mailbox.Maildir = fmt.Sprintf("%s@%s/", mailbox.LocalPart, mailbox.Domain)

	_, err = g.DB().Model("mailbox").Ctx(ctx).Insert(mailbox)
	if err != nil {
		if strings.Contains(err.Error(), "duplicate") || strings.Contains(err.Error(), "unique") {
			return fmt.Errorf("mailbox %s already exists", mailbox.Username)
		}
		return err
	}
	// maildirsize
	if e2 := ensureMaildirAndQuotaFile(ctx, mailbox); e2 != nil {
		g.Log().Warning(ctx, "ensureMaildirAndQuotaFile failed", e2)
	}
	return nil
}

func Update(ctx context.Context, mailbox *v1.Mailbox) (err error) {
	mailbox.UpdateTime = time.Now().Unix()
	if mailbox.Password != "" {
		mailbox.PasswordEncode = PasswdEncode(ctx, mailbox.Password)
		mailbox.Password, err = PasswdMD5Crypt(ctx, mailbox.Password)

		if err != nil {
			err = fmt.Errorf("Generate password md5-crypt failed: %w", err)

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Check mailbox existence (SELECT by lowercased username) before calling Add
  2. Catch this error and surface a friendly 'address already in use' message to the end user
  3. Handle concurrent creation with upsert (INSERT ... ON CONFLICT DO NOTHING) or a unique-check inside a transaction
  4. Purge soft-deleted/duplicate rows that still hold the unique key

Example fix

// before
_, err = g.DB().Model("mailbox").Ctx(ctx).Insert(mailbox)
if err != nil {
	if strings.Contains(err.Error(), "duplicate") || strings.Contains(err.Error(), "unique") {
		return fmt.Errorf("mailbox %s already exists", mailbox.Username)
	}
	return err
}
// after
count, err := g.DB().Model("mailbox").Ctx(ctx).Where("username = ?", mailbox.Username).Count()
if err != nil {
	return err
}
if count > 0 {
	return fmt.Errorf("mailbox %s already exists", mailbox.Username)
}
_, err = g.DB().Model("mailbox").Ctx(ctx).Insert(mailbox)
Defensive patterns

Strategy: try-catch

Validate before calling

count, err := g.DB().Model("mailbox").Ctx(ctx).
	Where("username = ?", strings.ToLower(username)).Count()
if err != nil {
	return err
}
if count > 0 {
	return fmt.Errorf("mailbox %s already exists", username)
}

Try / catch

err := mail_boxes.Add(ctx, mailbox)
if err != nil && strings.Contains(err.Error(), "already exists") {
	// friendly UX: surface 'address already in use' to the user
	return consts.NewDuplicateMailboxError(mailbox.Username)
}
if err != nil {
	return err
}

Prevention

When it happens

Trigger: Inserting a mailbox whose username already exists in the mailbox table (unique constraint); two concurrent Add calls racing to create the same address; re-running mailbox creation for an existing user; soft-deleted rows still occupying the unique key.

Common situations: User double-submits the create-mailbox form; import scripts re-run without idempotency; case-sensitivity mismatch — usernames are lowercased before insert but callers checked existence with mixed case; leftover row after an alias/domain rename.

Related errors


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