projectdiscovery/nuclei · error

invalid number of recipients: required 1, got %d

Error message

invalid number of recipients: required 1, got %d

What it means

Returned by smtp.Client.IsOpenRelay when the SMTPMessage does not have exactly one recipient: the check rejects len(msg.to) == 0 and len(msg.to) > 1 after the MAIL FROM command. The open-relay probe is intentionally minimal — one sender, one recipient — so the library enforces a single Rcpt call rather than guessing which recipient to test.

Source

Thrown at pkg/js/libs/smtp/smtp.go:156

	}

	addr := net.JoinHostPort(c.host, c.port)
	conn, err := dialer.Fastdialer.Dial(c.nj.Context(), "tcp", addr)
	if err != nil {
		return false, err
	}
	defer func() {
		_ = conn.Close()
	}()
	client, err := smtp.NewClient(conn, c.host)
	if err != nil {
		return false, err
	}
	if err := client.Mail(msg.from); err != nil {
		return false, err
	}
	if len(msg.to) == 0 || len(msg.to) > 1 {
		return false, fmt.Errorf("invalid number of recipients: required 1, got %d", len(msg.to))
	}
	if err := client.Rcpt(msg.to[0]); err != nil {
		return false, err
	}

	// Send the email body.
	wc, err := client.Data()
	if err != nil {
		return false, err
	}

	_, err = wc.Write([]byte(msg.String()))
	if err != nil {
		return false, err
	}
	err = wc.Close()
	if err != nil {
		return false, err

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. Set exactly one recipient on the message before IsOpenRelay
  2. Remove extra To/AddTo calls that inflate the recipient list
  3. If you built a multi-recipient message, construct a fresh single-recipient message for the relay probe

Example fix

// before
const msg = new smtp.SMTPMessage();
msg.From('attacker@evil.com');
msg.To('a@evil.com');
msg.To('b@evil.com');
client.IsOpenRelay(msg);

// after
const msg = new smtp.SMTPMessage();
msg.From('attacker@evil.com');
msg.To('a@evil.com');
client.IsOpenRelay(msg);
Defensive patterns

Strategy: validation

Validate before calling

const msg = new smtp.SMTPMessage();
msg.From(fromAddr);
msg.To(singleRecipient); // exactly one
// do not call To/AddTo again before IsOpenRelay
client.IsOpenRelay(msg);

Try / catch

try { client.IsOpenRelay(msg) } catch (e) { if (String(e).includes('invalid number of recipients')) { /* rebuild message with one recipient */ } else { throw e; } }

Prevention

When it happens

Trigger: Calling IsOpenRelay with a message where To() was never called (zero recipients); adding multiple recipients (To called twice, or an AddTo-style API used repeatedly) before the probe; copying message-building code from a send-email flow that legitimately has several recipients.

Common situations: Template authors reusing mail-composition helpers that add default recipients; forgetting the To step entirely; assuming the library loops over recipients like a real MTA submit.

Related errors


AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15). Data as JSON: /api/errors/11c6652e7206ed12. Report an issue: GitHub.