I'm frequently encountering this error in one of my Next.js serverless API routes:
"Uncaught Exception: Error [ERR_STREAM_WRITE_AFTER_END]"
This error suggests that there's an attempt to write to a response object after it has already been closed. However, based on my code, this shouldn't be happening.
Here's the API code:
import type { NextApiRequest, NextApiResponse } from 'next';import { CURRENT_ORIGIN } from '../../../common/envVar';import { trackEventMxp } from '../../../modules/analytics/serverAnalytics';import { sendEmailNodeRuntime } from '../../../modules/email/send';import { checkAuth, extractToken } from '../../../modules/firebase/auth';import { getUserFromId } from '../../../modules/firebase/firebaseHelper';import { getCustomTokenFromUid } from '../../../modules/firebase/localEncryption';import { sendSlackMessageToChannel } from '../../../modules/slack/slack';import { apiError, type ApiSchemaRequest, type ApiSchemaResponse } from '../../../shared/apiSchema';
type Response = ApiSchemaResponse['/api/auth/sendEmailVerificationMail'];type Body = ApiSchemaRequest['/api/auth/sendEmailVerificationMail']['fetchOptions']['body'];
export default async function sendEmailVerificationMail(req: NextApiRequest, res: NextApiResponse<Response>) { try { const userFromToken = await checkAuth(req, 'node-runtime', false);
if (!userFromToken) { const token = extractToken(req, 'node-runtime') || ''; throw new Error(`uid not found`); }
// get latest user info in case it has changed const user = (await getUserFromId(userFromToken.uid))!;
const body: Body = req.body; const { source } = body;
const { uid, email, displayName } = user;
if (user.emailVerified) { res.status(200).json({ data: null, error: null }); return; }
const token = getCustomTokenFromUid(uid);
const verifyLink = `${CURRENT_ORIGIN}/verify-email?token=${token}&source=${source}`;
const resp = await sendEmailNodeRuntime({ template: 'verify your email', email: email!, mailMergeParams: { name: displayName, verifyLink: verifyLink }, });
await trackEventMxp(user.uid, 'email_verification_link_sent', { email: user.email, });
console.log('sendEmailVerificationMail done '); res.status(200).json({ data: null, error: null }); } catch (e: any) { console.error('[sendEmailVerificationMail] catch error:', e);
// send verification failure to alerts await sendSlackMessageToChannel('alerts', `[sendEmailVerificationMail] catch error: ${e.message}`);
res.status(400).json({ data: null, error: apiError.unknown_error }); }}
full error log
ncaught Exception: Error [ERR_STREAM_WRITE_AFTER_END]: write after end at new NodeError (node:internal/errors:405:5) at ServerResponse.end (node:_http_outgoing:1017:15) at K.t.end (/var/task/node_modules/.pnpm/next@14.2.4_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.77.6/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:13847) at /var/task/node_modules/.pnpm/next@14.2.4_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.77.6/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:14667 at K.t.send (/var/task/node_modules/.pnpm/next@14.2.4_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.77.6/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:14676) at K.t.json (/var/task/node_modules/.pnpm/next@14.2.4_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.77.6/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:14759) at p (/var/task/.next/server/pages/api/auth/verifyEmail.js:1:3060) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async K (/var/task/node_modules/.pnpm/next@14.2.4_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.77.6/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:16853) at async U.render (/var/task/node_modules/.pnpm/next@14.2.4_react-dom@18.3.1_react@18.3.1__react@18.3.1_sass@1.77.6/node_modules/next/dist/compiled/next-server/pages-api.runtime.prod.js:20:17492) { code: 'ERR_STREAM_WRITE_AFTER_END'}Node.js process exited with exit status: 129. The logs above can help with debugging the issue.