diff --git a/packages/next/client/components/react-dev-overlay/internal/helpers/use-error-handler.ts b/packages/next/client/components/react-dev-overlay/internal/helpers/use-error-handler.ts index bd96c248ea0c..fa86bc92f443 100644 --- a/packages/next/client/components/react-dev-overlay/internal/helpers/use-error-handler.ts +++ b/packages/next/client/components/react-dev-overlay/internal/helpers/use-error-handler.ts @@ -22,9 +22,12 @@ function isHydrationError(error: Error): boolean { ) } -try { - Error.stackTraceLimit = 50 -} catch {} +if (typeof window !== 'undefined') { + try { + // Increase the number of stack frames on the client + Error.stackTraceLimit = 50 + } catch {} +} const errorQueue: Array = [] const rejectionQueue: Array = [] diff --git a/packages/next/server/dev/next-dev-server.ts b/packages/next/server/dev/next-dev-server.ts index 336686fbe51f..4f126062f435 100644 --- a/packages/next/server/dev/next-dev-server.ts +++ b/packages/next/server/dev/next-dev-server.ts @@ -150,6 +150,10 @@ export default class DevServer extends Server { } constructor(options: Options) { + try { + // Increase the number of stack frames on the server + Error.stackTraceLimit = 50 + } catch {} super({ ...options, dev: true }) this.persistPatchedGlobals() this.renderOpts.dev = true diff --git a/test/development/acceptance-app/ReactRefreshLogBox.test.ts b/test/development/acceptance-app/ReactRefreshLogBox.test.ts index 6e73c94dfa90..7b7a416c68bc 100644 --- a/test/development/acceptance-app/ReactRefreshLogBox.test.ts +++ b/test/development/acceptance-app/ReactRefreshLogBox.test.ts @@ -1153,4 +1153,72 @@ describe('ReactRefreshLogBox app', () => { await cleanup() }) + + test('Call stack count is correct for server error', async () => { + const { session, browser, cleanup } = await sandbox( + next, + new Map([ + [ + 'app/page.js', + ` + export default function Page() { + throw new Error('Server error') + } +`, + ], + ]) + ) + + expect(await session.hasRedbox(true)).toBe(true) + + // Open full Call Stack + await browser + .elementByCss('[data-nextjs-data-runtime-error-collapsed-action]') + .click() + const callStackCount = ( + await browser.elementsByCss('[data-nextjs-call-stack-frame]') + ).length + + // Expect more than the default amount of frames + // The default stackTraceLimit results in max 9 [data-nextjs-call-stack-frame] elements + expect(callStackCount).toBeGreaterThan(9) + + await cleanup() + }) + + test('Call stack count is correct for client error', async () => { + const { session, browser, cleanup } = await sandbox( + next, + new Map([ + [ + 'app/page.js', + ` + 'use client' + export default function Page() { + if (typeof window !== 'undefined') { + throw new Error('Client error') + } + return null + } +`, + ], + ]) + ) + + expect(await session.hasRedbox(true)).toBe(true) + + // Open full Call Stack + await browser + .elementByCss('[data-nextjs-data-runtime-error-collapsed-action]') + .click() + const callStackCount = ( + await browser.elementsByCss('[data-nextjs-call-stack-frame]') + ).length + + // Expect more than the default amount of frames + // The default stackTraceLimit results in max 9 [data-nextjs-call-stack-frame] elements + expect(callStackCount).toBeGreaterThan(9) + + await cleanup() + }) })