A stack trace returned in the HTTP response body hands an attacker your directory layout, framework version, ORM call signatures, and often the exact SQL that failed — all of which let them craft targeted follow-up payloads instead of probing blind. A PrismaClientKnownRequestError in a 500 response reveals your schema field names; a filesystem ENOENT reveals your server paths; a module-not-found error reveals your dependency versions to anyone looking for a matching CVE. AI coding tools produce this failure mode when a developer asks them to "show me why this route is broken" and they wire error.stack into the JSON response "so the frontend can display it," without gating on NODE_ENV or remembering to remove the debug scaffold before shipping.
Info because leaked stack traces aid reconnaissance but rarely produce direct compromise on their own — the impact depends on what subsequent vulnerabilities the exposed internals help discover.
Return a generic message and log the stack server-side:
catch (e) {
console.error(e) // server-side log
return NextResponse.json({ error: 'Internal error' }, { status: 500 })
}
Deeper remediation guidance and cross-reference coverage for this check lives in the data-protection Pro audit — run that after applying this fix for a more exhaustive pass on the same topic.
project-snapshot.data-exposure.error-pages-no-stack-tracesinforeturn new Response(error.stack), res.json({ error: error.stack }), console.log(error.stack) in handlers without env-gating. Also check Next.js custom error pages (app/error.tsx, pages/_error.tsx) for unconditional stack rendering.error.message may be returned; full error.stack may not.error.stack in the response body without a NODE_ENV !== 'production' gate."Scanned N error handlers; 0 leak stack traces in production responses.""app/api/checkout/route.ts catches errors and returns { error: e.stack } unconditionally".catch (e) {
console.error(e) // server-side log
return NextResponse.json({ error: 'Internal error' }, { status: 500 })
}