Go 的 net/smtp 并不禁止邮箱地址中包含加号(如 ),报错“Transaction failed: Illegal semicolon, not in group”实际源于邮件正文头部的 To 字段误用分号分隔多个收件人,而非加号本身导致的问题。
go 的 net/smtp 并不禁止邮箱地址中包含加号(如
在 Go 中使用 net/smtp.SendMail 发送邮件时,邮箱地址中的加号(+)完全合法且无需特殊处理——它属于 RFC 5322 允许的本地部分(local-part)字符,Go 的 net/mail 和 net/smtp 均原生支持。真正触发 Transaction failed: Illegal semicolon, not in group 错误的原因,是构造邮件原始内容(即 msg 字节切片)时,在 To: 头部错误地使用了分号(;)分隔多个收件人。
SMTP 协议规范(RFC 5321)明确要求:邮件头字段(如 To, Cc)中的地址列表必须用逗号(,)分隔,而非分号。分号在 SMTP 中具有特殊语义(常用于组地址语法),若出现在非预期位置,远程邮件服务器会拒绝该事务并返回上述错误。
✅ 正确做法示例:
package main
import (
"fmt"
"net/smtp"
"net/mail"
"strings"
)
func sendEmail(toAddresses []string, from, subject, body, smtpAddr string, auth smtp.Auth) error {
// 构建标准邮件头(注意:To 字段用逗号分隔,非分号!)
toHeader := strings.Join(toAddresses, ", ")
msg := fmt.Sprintf(
"To: %s\r\n"+
"From: %s\r\n"+
"Subject: %s\r\n"+
"MIME-Version: 1.0\r\n"+
"Content-Type: text/plain; charset=\"utf-8\"\r\n\r\n"+
"%s",
toHeader,
from,
subject,
body,
)
return smtp.SendMail(smtpAddr, auth, from, toAddresses, []byte(msg))
}
// 使用示例:含 + 号的邮箱完全可用
func main() {
to := []string{
"<EMAIL>", // 合法:含 + 号
"<EMAIL>", // 多个地址,用逗号分隔
}
err := sendEmail(
to,
"<EMAIL>",
"欢迎注册",
"感谢您的注册!",
"smtp.gmail.com:587",
smtp.PlainAuth("", "<EMAIL>", "app-password", "smtp.gmail.com"),
)
if err != nil {
panic(err)
}
}⚠️ 关键注意事项:
- 永远不要在 To: 头中使用分号(;)分隔地址——这是最常见误因;
- 若需支持国际化邮箱或复杂格式,建议使用 mail.Address 结构体 + Address.String() 方法生成合规地址字符串,避免手动拼接;
- smtp.SendMail 的第四个参数(to)是收件人地址切片,仅用于 SMTP RCPT TO 命令;而邮件头中的 To: 字段(第五个参数 msg 内容)是纯展示用途,二者需保持逻辑一致,但语法独立;
- 加号邮箱(如 <EMAIL>)在 Gmail 等服务商中常用于标签化过滤,Go 程序无需转义或编码,直接传递原始字符串即可。
总结:该错误与 + 字符无关,本质是邮件协议语法违规。严格遵守 RFC 规范——头部地址列表用逗号分隔、避免分号滥用,即可彻底解决此问题,并安全支持所有合法邮箱格式。


















