You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nodemailer发送Gmail数日后出现invalid_grant报错如何解决

失效原因
  • 核心触发原因是你的Google Cloud OAuth应用处于测试状态:未正式发布的测试类OAuth应用,生成的refresh token有效期固定为7天,到期后自动失效,就会返回invalid_grant错误,这也是你替换新token后就能恢复正常的直接原因。
  • 其他可能触发的场景:同一应用同一账号生成的refresh token超过50个的数量上限,最早生成的token会被自动失效;账号密码修改、授权被手动撤销也会触发该错误。
解决方案
  • 调整OAuth应用发布状态:进入Google Cloud控制台的API和服务模块,打开OAuth同意屏幕配置,将发布状态从「测试」修改为「正式发布」。个人自用场景下调整为正式发布无需审核,调整后refresh token就不会7天自动过期。
  • 控制refresh token生成次数:不要反复生成新的refresh token,确认应用配置正确后仅保留1个有效token使用即可,避免因数量上限导致旧token提前失效。
  • 代码逻辑优化:你现有的代码可以复用transport实例,无需每次发邮件都重新创建,减少不必要的资源消耗,同时可以增加invalid_grant错误的捕获逻辑,出现异常时主动告警便于及时处理,优化参考代码如下:
const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID
const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET
const GOOGLE_REDIRECT_URI = process.env.GOOGLE_REDIRECT_URI
const GOOGLE_REFRESH_TOKEN = process.env.GOOGLE_REFRESH_TOKEN
const GMAIL_USER = 'user@gmail.com'

const oAuth2Client = new google.auth.OAuth2(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REDIRECT_URI)
oAuth2Client.setCredentials({refresh_token: GOOGLE_REFRESH_TOKEN})

// 全局复用transport实例
let mailTransport: nodemailer.Transporter

async function getTransport() {
  if (mailTransport) return mailTransport
  const accessToken = await oAuth2Client.getAccessToken()
  mailTransport = nodemailer.createTransport({
    service: 'gmail',
    auth: {
      type: 'OAUTH2',
      user: GMAIL_USER,
      clientId: GOOGLE_CLIENT_ID,
      clientSecret: GOOGLE_CLIENT_SECRET,
      refreshToken: GOOGLE_REFRESH_TOKEN,
      accessToken,
    }
  })
  return mailTransport
}

export async function sendMail(payload: IMailDTO): Promise<SMTPTransport.SentMessageInfo>{
  const data = MailDTO.receiver(payload)
  const transport = await getTransport()

  const mailOptions = {
    from: 'no-reply <no-reply@gmail.com>',
    to: data.to,
    subject: data.subject,
    text: data.text,
    html: data.html
  }

  try {
    const result = await transport.sendMail(mailOptions)
    return result
  } catch (err) {
    // 捕获invalid_grant错误,可自行添加告警逻辑
    if (err?.message?.includes('invalid_grant')) {
      // 重置transport,下次请求会重新获取access token
      mailTransport = null
      throw new Error('Refresh token失效,请检查OAuth应用配置')
    }
    throw new Error("Email not sent. Try again.")
  }
}

内容的提问来源于stack exchange,提问作者Prasanga Thapaliya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 19:42:01