发送邮箱验证链接时遇“Can't set headers after they are sent”错误求助
Hey there! That classic Can't set headers after they are sent error is one of the most common gotchas when working with Express—let’s break down exactly what’s happening and how to fix it for your email verification flow.
What’s Causing This?
This error pops up when your Express server tries to send more than one HTTP response for a single client request. For example:
- You call
res.send()orres.json()to confirm the verification email was sent, but then another piece of code (like an async callback or unguarded logic) tries to send another response later. - An unhandled Promise rejection (from your email-sending code) is causing unexpected code execution that triggers a duplicate response.
The "Unhandled rejection" error is tied to this too—your email-sending Promise is failing, but you aren’t catching that failure, so it’s spilling over and messing up your response flow.
Step-by-Step Fixes
1. Ensure Only One Response Per Request
Double-check your verification route code for accidental duplicate responses. Here’s a common mistake to watch out for:
// ❌ Wrong: Sends two responses app.post('/send-verification', (req, res) => { sendVerificationEmail(req.user.email) .then(() => { res.json({ success: true }); // First response }); res.json({ message: "Verification email queued" }); // Second response—this causes the error! });
Fix it by making sure your response logic is properly contained in the Promise chain, and use return to stop further code execution:
// ✅ Correct: Single response, with proper flow control app.post('/send-verification', (req, res) => { sendVerificationEmail(req.user.email) .then(() => { return res.json({ success: true }); // Return ensures no further code runs here }) .catch(err => { return res.status(500).json({ error: "Failed to send verification email" }); }); });
2. Catch All Promise Rejections
The "Unhandled rejection" error means your email-sending Promise is throwing an error that isn’t being caught. Use async/await with try/catch for cleaner, more reliable error handling:
// ✅ Even better: Async/Await with Try/Catch app.post('/send-verification', async (req, res) => { try { await sendVerificationEmail(req.user.email); return res.json({ success: true }); } catch (err) { console.error("Email send failure:", err); // Log the error for debugging return res.status(500).json({ error: "Could not send verification email" }); } });
This way, any errors in your email-sending logic are properly handled, and you won’t get unhandled rejection warnings or accidental duplicate responses.
3. Check for Middleware Conflicts
If you’re using middleware, make sure none of your previous middleware functions are already sending a response for the same request. For example, a validation middleware that sends an error response but doesn’t stop the request from reaching your verification route.
Quick Recap
- Rule #1: Every request gets exactly one response—no exceptions.
- Rule #2: Always catch errors in async/Promise code to avoid unhandled rejections.
- Use
returnafter sending responses to prevent unintended code execution.
内容的提问来源于stack exchange,提问作者Fenil Shah

