yagmail使用GMail OAuth2认证数天后失效的解决方法
问题根因
抛出HTTP Error 400: Bad Request的核心原因是你存储的Google OAuth2刷新令牌(refresh_token)已失效,和yagmail库本身的凭证刷新逻辑无关。Google侧会在特定规则下主动作废已签发的refresh_token,你遇到的每隔数天固定复现故障的情况,绝大多数是因为你的OAuth应用处于「测试」发布状态——测试状态下Google签发的所有refresh_token有效期固定为7天,到期直接作废,和你描述的故障周期完全匹配。
其他会触发refresh_token非预期失效的常见场景:
- 同一Google账号对同一OAuth客户端累计签发的有效refresh_token超过50个,最早生成的token会被自动回收
- 账号修改密码、触发Google安全风控(如非常用IP连续登录、判定为异常发信行为),所有已签发的refresh_token会被批量作废
- 授权流程未正确申请离线权限,拿到的是短效refresh_token
解决方案
按以下步骤调整即可彻底解决定期需要手动重生成凭证的问题:
1. 将OAuth应用切换为生产发布状态
这是解决7天固定过期问题的核心操作:
- 登录Google Cloud控制台,进入对应项目的「API和服务」-「OAuth同意屏幕」页面
- 如果当前应用状态显示为「测试中」,直接点击「发布应用」按钮,将状态切换为「生产中」
- 仅内部使用、只申请
https://mail.google.com/发信权限的应用不需要提交Google审核,切换状态后立即生效,签发的refresh_token不再有7天有效期限制
2. 重新生成长期有效的refresh_token
切换应用状态后,重新走一次OAuth授权流程生成新的凭证文件,注意授权请求必须携带两个参数,否则拿不到长期有效token:
access_type=offline:告知Google需要返回可用于后台自动刷新凭证的refresh_tokenprompt=consent:强制弹出授权确认页,避免Google返回之前缓存的旧短效token
用新流程生成的credentials.json替换服务器上的旧文件即可,正常使用场景下这个refresh_token不会自动过期。
3. 可选兜底优化
可以在发信逻辑里加一层异常捕获,避免极端风控场景下业务完全无感知:
import yagmail from urllib.error import HTTPError def send_with_yagmail(self): try: yag = yagmail.SMTP(self.our_email, oauth2_file="/<path_to>/credentials.json") yag.send(self.to_email, self.message_subject, contents=self.message_body) except HTTPError as e: if e.code == 400: # 可在此处接入告警,通知管理员及时更新凭证 raise RuntimeError("Gmail发信认证失效,请更新OAuth2凭证") from e
注意事项
- 不要在多套环境(本地、测试机、生产机)反复对同一个客户端/账号触发授权生成新的refresh_token,单账号单客户端最多保留50个有效refresh_token,超额后旧token会被自动作废
- 生产环境尽量固定发信服务的出口IP,避免频繁跨地域登录触发Google安全风控
- 不要用该Gmail账号发送垃圾邮件、短时间批量发信,否则会触发风控直接作废所有凭证
内容的提问来源于stack exchange,提问作者mircealungu
相关产品推荐
相关产品推荐

