Firebase路由返回错误状态后仍执行后续逻辑引发响应错误
Hey there, let's break down why your code is hitting that frustrating ERR_HTTP_HEADERS_SENT error and how to fix it!
The Root Cause
The problem lies in how your Promise chain handles the "scream not found" scenario. When you return res.status(404).json({ error: 'Scream not found' }) in the first .then() callback, you're sending a response—but you're also returning an Express Response object, not a rejected Promise.
Promise chains treat any non-rejected value as a success, so the next .then() callbacks (adding the comment and sending another response) still run. This means you're sending a 404 response first, then trying to send a second response with the new comment—hence the error about setting headers after a response has already been sent.
Solution 1: Use Async/Await (Cleanest Approach)
Async/await makes control flow linear and easier to follow, which avoids this kind of Promise chain misstep. Here's how to refactor your code:
exports.commentOnScream = async (req, res) => { // Validate comment body first if (req.body.body.trim() === '') { return res.status(400).json({ comment: 'Must not be empty' }); } const newComment = { body: req.body.body, createdAt: new Date().toISOString(), screamId: req.params.screamId, userHandle: req.user.handle, userImage: req.user.imageUrl }; try { // Fetch the scream document const screamDoc = await db.doc(`/screams/${req.params.screamId}`).get(); // If scream doesn't exist, return 404 and exit immediately if (!screamDoc.exists) { return res.status(404).json({ error: 'Scream not found' }); } // Update comment count await screamDoc.ref.update({ commentCount: screamDoc.data().commentCount + 1 }); // Add new comment to collection await db.collection('comments').add(newComment); // Return success response return res.json(newComment); } catch (err) { console.log(err); return res.status(500).json({ error: 'Something went wrong' }); } };
With async/await, when we hit return res.status(404)..., the function exits right away—none of the subsequent code (updating counts, adding comments) runs, so we only send one response.
Solution 2: Fix the Promise Chain
If you prefer sticking with .then() syntax, you need to interrupt the chain when the scream isn't found. Throw a custom error to jump directly to the .catch() block instead of returning a response:
exports.commentOnScream = (req, res) => { if (req.body.body.trim() === '') return res.status(400).json({ comment: 'Must not be empty' }); const newComment = { body: req.body.body, createdAt: new Date().toISOString(), screamId: req.params.screamId, userHandle: req.user.handle, userImage: req.user.imageUrl }; db.doc(`/screams/${req.params.screamId}`) .get() .then((doc) => { if (!doc.exists) { // Throw an error to stop the chain and trigger catch throw new Error('Scream not found'); } else { return doc.ref.update({ commentCount: doc.data().commentCount + 1 }); } }) .then(() => { return db.collection('comments').add(newComment); }) .then(() => { return res.json(newComment); }) .catch((err) => { console.log(err); // Check for our custom error to return the right status code if (err.message === 'Scream not found') { return res.status(404).json({ error: err.message }); } return res.status(500).json({ error: 'Something went wrong' }); }); };
Throwing an error skips all remaining .then() callbacks and sends us straight to .catch(), where we can handle the 404 case without conflicting responses.
内容的提问来源于stack exchange,提问作者code.cycling

