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

NodeJS报Cannot set headers after they are sent错误如何解决

错误根因

Error [ERR_HTTP_HEADERS_SENT] 报错的本质是同一个HTTP请求流程中,服务端多次向客户端发送响应(即多次调用res.json()/res.send()/res.end()等方法),Express 不允许重复设置响应头/返回响应。

代码问题点

问题出在sendResetPasswordMail的错误处理逻辑:

  • 当发邮件出现异常时,sendResetPasswordMail内部的catch块会调用catchError(err, next),正常情况下这个工具方法会调用Express错误处理中间件,直接向客户端返回错误响应。
  • 但sendResetPasswordMail的catch块捕获错误后没有向外抛出异常,所以上层Controller的await sendResetPasswordMail(...)不会触发自身的catch块,会继续向下执行res.status(200).json({ message: SuccessMessages.resetPassword }),此时就出现了两次返回响应的情况,触发报错。
修复方案

可二选一:

方案1:移除sendResetPasswordMail内部的错误捕获,把异常完全交给上层Controller处理

修改后sendResetPasswordMail代码如下:

const sendResetPasswordMail: SendResetPasswordMail = async (userEmail: string, token: string): Promise<void> => {
  const transporter = nodemailer.createTransport({
    service: 'gmail',
    auth: {
      user: adminEmail.email,
      pass: adminEmail.pass
    }
  })
  const mailOptions = {
    from: adminEmail.email,
    to: userEmail,
    subject: 'Reset password',
    text: `Reset password: http://localhost:3000/resetPassword?token=${token}`
  }
  await transporter.sendMail(mailOptions)
}

调用时也不需要传入next参数即可,发邮件异常会直接进入Controller的catch块走统一错误处理,不会继续执行后续200返回逻辑。

方案2:在sendResetPasswordMail的catch块抛出异常,通知上层执行出错

修改后sendResetPasswordMail的catch块代码如下:

catch (err: any) {
  catchError(err, next)
  throw err // 抛出错误,中断上层后续逻辑执行
}
额外校验建议

可以确认下catchError工具方法的实现,确保它确实会调用res相关方法返回响应,这是触发本次报错的前提。

内容的提问来源于stack exchange,提问作者George Hutanu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 15:54:01