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.