Node.js项目添加认证时出现Error [ERR_HTTP_HEADERS_SENT]错误的解决求助
Hey there! Let's dig into why you're running into this Error [ERR_HTTP_HEADERS_SENT] issue. I've gone through your code and the root cause is pretty clear—you're accidentally sending multiple HTTP responses to the client, which Node.js blocks to prevent inconsistent behavior.
What's Causing the Error?
The core problem lies in your authUser middleware:
- Whenever there's an authentication failure (invalid token, missing user, no token present), you send an error response with
res.status().json(), but you still callnext()afterwards. - This means even after telling the client "invalid token", you're letting the request proceed to your
fetchOnecontroller, which then tries to send another response (either the data or a 404). Sending two responses to the same request triggers theERR_HTTP_HEADERS_SENTerror.
Fix #1: Correct the Authentication Middleware
You need to ensure that only successful authentication triggers next(), and you stop execution immediately after sending an error response. Here's the fixed middleware:
const authUser = (req, res, next) => { console.log("Auth middleware"); if (req.headers && req.headers.authorization && req.headers.authorization.split(" ")[0] === "JWT") { jwt.verify( req.headers.authorization.split(" ")[1], process.env.JWT_SECRET, function (err, verifyToken) { if (err) { // Send error response and stop further execution return res.status(401).json({ message: "Invalid JWT Token" }); } userModel .findById(verifyToken.id) .then((user) => { if (!user) { return res.status(401).json({ message: "Invalid User" }); } // Optional: Attach user to request for use in controllers req.user = user; // Only call next() if authentication succeeds next(); }) .catch((err) => { return res.status(500).json({ message: err.message || "Server Error" }); }); } ); } else { // Send error and stop execution return res.status(403).json({ message: "Token not present" }); } };
Key changes here:
- Added
returnafter every error response to halt the middleware function. - Moved
next()inside the successful user lookup block—only called when authentication is fully valid.
Fix #2: Improve Your Controller's Error Handling
Your fetchOne controller has a small issue too: the try/catch won't catch errors from the findById().then() Promise chain because it's asynchronous. Let's fix that either with proper Promise error handling or async/await:
Option A: Promise Chain with Proper Error Handling
exports.fetchOne = (req, res) => { const _id = req.params.id; console.log("fetch One"); collectionsModel.findById(_id) .then((data) => { if (!data) { return res.status(404).json({ message: "Data not found" }); } console.log(data); res.send(data); }) .catch((error) => { console.log(error.message); // Send error response to client instead of just logging res.status(500).json({ message: error.message || "Failed to fetch data" }); }); };
Option B: Async/Await with Try/Catch
exports.fetchOne = async (req, res) => { const _id = req.params.id; console.log("fetch One"); try { const data = await collectionsModel.findById(_id); if (!data) { return res.status(404).json({ message: "Data not found" }); } console.log(data); res.send(data); } catch (error) { console.log(error.message); res.status(500).json({ message: error.message || "Failed to fetch data" }); } };
Why This Works
- The middleware now only passes valid requests to the controller, so you never get duplicate responses.
- The controller ensures exactly one response is sent per request, whether it's the requested data, a 404, or a server error.
Give these changes a try—your authentication should work correctly, and the ERR_HTTP_HEADERS_SENT error should disappear!
备注:内容来源于stack exchange,提问作者GOKULNATH RS

