knadh/listmonk · error

subscribers.invalidEmail

Error message

subscribers.invalidEmail

What it means

Importer.SanitizeEmail wraps utils.SanitizeEmail; when bare-address validation fails (malformed email), it replaces the low-level error with the i18n message 'subscribers.invalidEmail' so import errors are localized. It is thrown per-row during subscriber import when the email value cannot be parsed as a valid address.

Source

Thrown at internal/subimporter/importer.go:614

		im.Unlock()

		return
	}

	select {
	case im.stop <- true:
		im.setStatus(StatusStopping)
	default:
	}
}

// SanitizeEmail validates and sanitizes an e-mail string and returns the
// canonical (lowercased, trimmed) address. Domain allowlist/blocklist rules
// are enforced on top of the bare-address validation in utils.SanitizeEmail.
func (im *Importer) SanitizeEmail(email string) (string, error) {
	addr, err := utils.SanitizeEmail(email)
	if err != nil {
		return "", errors.New(im.i18n.T("subscribers.invalidEmail"))
	}

	// Check if the e-mail's domain is blocklisted. The e-mail domain and blocklist config
	// are always lowercase.
	if im.hasAllowlist || im.hasBlocklist {
		d := strings.Split(addr, "@")
		if len(d) != 2 {
			return addr, nil
		}

		domain := d[1]

		// If there's an allowlist, check if the domain is in it. Checking blocklist after that is moot.
		if im.hasAllowlist {
			if !im.checkInList(domain, im.hasAllowlistWildcards, im.domainAllowlist) {
				return "", errors.New(im.i18n.T("subscribers.domainBlocklisted"))
			}
		} else if im.hasBlocklist {

View on GitHub (pinned to 670c01717d)

Solutions

  1. Correct or remove rows with malformed email values in the CSV before importing.
  2. Pre-validate the file with a script using the same address syntax check (e.g. Go net/mail ParseAddress).
  3. Trim whitespace and strip stray characters (commas, quotes) from the email column.
  4. If valid emails are rejected, verify the input encoding (BOM, smart quotes) isn't corrupting values.

Example fix

// before
email
john acme.com
// after
email
john@acme.com
Defensive patterns

Strategy: validation

Validate before calling

email := strings.TrimSpace(row.Email)
if email == "" || len(email) > 1000 {
    return fmt.Errorf("row %d: empty or oversized email", i)
}
if _, err := mail.ParseAddress(email); err != nil {
    return fmt.Errorf("row %d: malformed email %q", i, email)
}

Type guard

func isValidEmail(s string) bool {
    _, err := mail.ParseAddress(strings.TrimSpace(s))
    return err == nil && !strings.Contains(s, " ")
}

Try / catch

em, err := importer.SanitizeEmail(email)
if err != nil {
    log.Printf("skipping row: %v", err) // localized 'subscribers.invalidEmail'
    continue
}

Prevention

When it happens

Trigger: ValidateFields calls SanitizeEmail on each imported row and the email string fails utils.SanitizeEmail — empty string, missing '@', invalid characters, unbalanced quotes, or invalid domain format.

Common situations: Dirty third-party lists with values like 'john(at)acme.com', trailing spaces/commas, concatenated fields, or Excel-mangled emails; also rows where column shift put a name in the email cell.

Related errors


AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01). Data as JSON: /api/errors/de2c32aab82ecb7e. Report an issue: GitHub.