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

调用res.status().json()触发ERR_HTTP_HEADERS_SENT错误的原因是什么?

问题根因

这个ERR_HTTP_HEADERS_SENT报错没有特殊逻辑,本质就是同一个请求你发了不止一次HTTP响应。HTTP协议规定一个请求只能对应一个响应,第一次响应发出去之后响应头就已经写入TCP流发给客户端了,后面再尝试修改响应头、写入响应体就会抛出这个错误。

结合你提到的「正确用户名+错误密码必现,正确密码时接口正常」的复现条件,问题出在argon2.verify的调用逻辑上:
你默认verify只会返回true/false两种结果,但实际上只要传入的第一个参数不是合法的argon2哈希(比如哈希是bcrypt等其他算法生成的、哈希串被截断修改、生成哈希时用的argon2版本和当前项目安装的版本不兼容),verify根本不会返回布尔值,而是直接抛出异常。
Express 4.x 默认无法捕获async路由中抛出的异步异常,你又没写try/catch捕获这个错误,异常抛出后,要么是框架默认逻辑先给客户端返回了500错误响应,要么是你自己加的全局异常监听先返回了错误,这时候第一次响应已经发送完成,后续失控的流程又走到你写的401响应逻辑,尝试第二次发送响应就直接触发崩溃。
另外你代码里虽然写了return,但没有和异常处理逻辑配合,只要异步逻辑抛错,return根本执行不到,流程会直接穿透。

修复方法
  1. 给verify逻辑加异常捕获,所有返回响应的地方都直接写成return res.xxx的形式,从写法上杜绝流程穿透。
  2. 检查users.json里存储的password_hash是否为合法的argon2格式,确认哈希生成环境和当前运行环境的argon2版本兼容。

修复后的核心代码:

router.post("/", async (req, res) => {
    const { phone_number, password } = req.body;
    const user = users.find((user) => user.phone_number === phone_number);

    if (!user) {
        return res.status(401).json({ message: "Invalid username/password" });
    }

    let isPasswordValid = false;
    try {
        isPasswordValid = await verify(user.password_hash, password);
    } catch (verifyErr) {
        console.error("密码校验异常:", verifyErr);
        return res.status(500).json({ message: "服务内部错误" });
    }

    if (!isPasswordValid) {
        return res.status(401).json({ message: "Invalid username/password" });
    }

    const { id } = user;
    sign({ id }, key, { expiresIn }, (err, token) => {
        if (err) {
            return res.status(500).json({ message: "Something went wrong" });
        }
        return res.status(200).json({ token });
    });
});

后续写async路由建议统一用包装函数处理异步异常,不要裸写async路由,不然很容易出现这种流程失控的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 11:39:28