From 742e272ade620c024d9a105bad3ba9c81c8780da Mon Sep 17 00:00:00 2001
From: ZeroPointSix <229769386+ZeroPointSix@users.noreply.github.com>
Date: Sun, 22 Mar 2026 18:12:49 +0800
Subject: [PATCH 1/4] fix: show error details in preview environments
---
apps/site/app/[locale]/error.tsx | 25 +++++++++++++-
apps/site/next.constants.mjs | 7 ++++
apps/site/tests/errorPage.test.jsx | 53 ++++++++++++++++++++++++++++++
3 files changed, 84 insertions(+), 1 deletion(-)
create mode 100644 apps/site/tests/errorPage.test.jsx
diff --git a/apps/site/app/[locale]/error.tsx b/apps/site/app/[locale]/error.tsx
index 0c4bf7f827ec8..12a77c2af1be6 100644
--- a/apps/site/app/[locale]/error.tsx
+++ b/apps/site/app/[locale]/error.tsx
@@ -4,11 +4,22 @@ import { useTranslations } from 'next-intl';
import Button from '#site/components/Common/Button';
import GlowingBackdropLayout from '#site/layouts/GlowingBackdrop';
+import { SHOW_ERROR_DETAILS } from '#site/next.constants.mjs';
import type { FC } from 'react';
-const ErrorPage: FC<{ error: Error }> = () => {
+type ErrorPageProps = {
+ error: Error & { digest?: string };
+};
+
+const ErrorPage: FC
+ {errorDetails}
+
+
diff --git a/apps/site/next.config.mjs b/apps/site/next.config.mjs
index 40d1f89e87cd3..30447adaa239e 100644
--- a/apps/site/next.config.mjs
+++ b/apps/site/next.config.mjs
@@ -29,6 +29,10 @@ const nextConfig = {
basePath: BASE_PATH,
// Vercel/Next.js Image Optimization Settings
images: getImagesConfig(),
+ env: {
+ NEXT_PUBLIC_VERCEL_ENV:
+ process.env.NEXT_PUBLIC_VERCEL_ENV || process.env.VERCEL_ENV,
+ },
serverExternalPackages: ['twoslash'],
outputFileTracingIncludes: {
// Twoslash needs TypeScript declarations to function, and, by default, Next.js
diff --git a/apps/site/next.constants.mjs b/apps/site/next.constants.mjs
index cf9b0c3ad5207..b960c103fdb00 100644
--- a/apps/site/next.constants.mjs
+++ b/apps/site/next.constants.mjs
@@ -14,12 +14,17 @@ export const IS_DEV_ENV = process.env.NODE_ENV === 'development';
*/
export const VERCEL_ENV = process.env.VERCEL_ENV || undefined;
+/**
+ * Public-facing Vercel environment, safe to use in client-side code.
+ */
+export const PUBLIC_VERCEL_ENV =
+ process.env.NEXT_PUBLIC_VERCEL_ENV || VERCEL_ENV;
+
/**
* Error details should only be exposed in local development or Vercel preview
* deployments, never in production.
*/
-export const SHOW_ERROR_DETAILS =
- process.env.NODE_ENV === 'development' || VERCEL_ENV === 'preview';
+export const SHOW_ERROR_DETAILS = IS_DEV_ENV || PUBLIC_VERCEL_ENV === 'preview';
/**
* This is used for telling Next.js to do a Static Export Build of the Website
diff --git a/apps/site/tests/errorPage.test.jsx b/apps/site/tests/errorPage.test.jsx
index 035bc4e72f81a..6fd30ef5b0676 100644
--- a/apps/site/tests/errorPage.test.jsx
+++ b/apps/site/tests/errorPage.test.jsx
@@ -4,7 +4,7 @@ import { describe, it } from 'node:test';
import { render, screen } from '@testing-library/react';
describe('ErrorPage', () => {
- it('renders technical details in preview environments', async t => {
+ const setupErrorPage = async (t, showErrorDetails, suffix = '') => {
t.mock.module('#site/components/Common/Button', {
defaultExport: ({ children, href }) => {children},
});
@@ -15,11 +15,15 @@ describe('ErrorPage', () => {
t.mock.module('#site/next.constants.mjs', {
namedExports: {
- SHOW_ERROR_DETAILS: true,
+ SHOW_ERROR_DETAILS: showErrorDetails,
},
});
- const { default: ErrorPage } = await import('../app/[locale]/error.tsx');
+ return import(`../app/[locale]/error.tsx${suffix}`);
+ };
+
+ it('renders technical details in preview environments', async t => {
+ const { default: ErrorPage } = await setupErrorPage(t, true);
render(
{
'layouts.error.backToHome'
);
assert.equal(
- screen.getByText('components.downloadReleasesTable.details').textContent,
- 'components.downloadReleasesTable.details'
+ screen.getByText('layouts.error.details').textContent,
+ 'layouts.error.details'
);
assert.match(
screen.getByText(/Preview deployment failed/).textContent,
@@ -50,4 +54,24 @@ describe('ErrorPage', () => {
/digest: abc123/i
);
});
+
+ it('hides technical details when the flag is disabled', async t => {
+ const { default: ErrorPage } = await setupErrorPage(
+ t,
+ false,
+ '?show-error-details-disabled'
+ );
+
+ render(
+
+ );
+
+ assert.equal(screen.queryByText('layouts.error.details'), null);
+ assert.equal(screen.queryByText(/Production should stay generic/), null);
+ assert.equal(screen.queryByText(/digest: hidden123/i), null);
+ });
});
diff --git a/packages/i18n/src/locales/en.json b/packages/i18n/src/locales/en.json
index a794c5f164efc..a267da0a71a79 100644
--- a/packages/i18n/src/locales/en.json
+++ b/packages/i18n/src/locales/en.json
@@ -370,6 +370,7 @@
"title": "Internal Server Error",
"description": "This page has thrown a non-recoverable error."
},
+ "details": "Details",
"backToHome": "Back to Home"
},
"download": {
diff --git a/packages/i18n/src/locales/es.json b/packages/i18n/src/locales/es.json
index dd15732fcecb6..0e682fd41276c 100644
--- a/packages/i18n/src/locales/es.json
+++ b/packages/i18n/src/locales/es.json
@@ -275,6 +275,7 @@
"title": "Error interno del servidor",
"description": "Esta página ha generado un error no recuperable."
},
+ "details": "Detalles",
"backToHome": "Volver al inicio"
},
"download": {
diff --git a/packages/i18n/src/locales/fr.json b/packages/i18n/src/locales/fr.json
index 851616fe1f311..feb232f960240 100644
--- a/packages/i18n/src/locales/fr.json
+++ b/packages/i18n/src/locales/fr.json
@@ -370,6 +370,7 @@
"title": "Erreur interne du serveur",
"description": "Cette page a généré une erreur irrécupérable."
},
+ "details": "Détails",
"backToHome": "Retourner à l'accueil"
},
"download": {
diff --git a/packages/i18n/src/locales/id.json b/packages/i18n/src/locales/id.json
index e5ceffe8aa3a4..f5cce098415a4 100644
--- a/packages/i18n/src/locales/id.json
+++ b/packages/i18n/src/locales/id.json
@@ -360,6 +360,7 @@
"title": "Kesalahan Server Internal",
"description": "Halaman ini mengalami error yang tidak dapat diperbaiki."
},
+ "details": "Rincian",
"backToHome": "Kembali ke Beranda"
},
"download": {
diff --git a/packages/i18n/src/locales/ja.json b/packages/i18n/src/locales/ja.json
index 6e6080d50e266..d2dafc34014f9 100644
--- a/packages/i18n/src/locales/ja.json
+++ b/packages/i18n/src/locales/ja.json
@@ -370,6 +370,7 @@
"title": "内部サーバーエラー",
"description": "このページで回復不可能なエラーが発生しました。"
},
+ "details": "詳細",
"backToHome": "ホームに戻る"
},
"download": {
diff --git a/packages/i18n/src/locales/ko.json b/packages/i18n/src/locales/ko.json
index 591a7b1422647..810deee83d034 100644
--- a/packages/i18n/src/locales/ko.json
+++ b/packages/i18n/src/locales/ko.json
@@ -249,6 +249,7 @@
"title": "Internal Server Error",
"description": "이 페이지에서 복구할 수 없는 오류가 발생했습니다."
},
+ "details": "세부정보",
"backToHome": "홈으로 돌아가기"
},
"download": {
diff --git a/packages/i18n/src/locales/pt-br.json b/packages/i18n/src/locales/pt-br.json
index de37d98e132e7..643069c8779cc 100644
--- a/packages/i18n/src/locales/pt-br.json
+++ b/packages/i18n/src/locales/pt-br.json
@@ -366,6 +366,7 @@
"title": "Erro Interno do Servidor",
"description": "Esta página lançou um erro não recuperável."
},
+ "details": "Detalhes",
"backToHome": "Voltar para a Página Inicial"
},
"download": {
diff --git a/packages/i18n/src/locales/pt.json b/packages/i18n/src/locales/pt.json
index 643d8e2def5c1..cb5783e2f0c59 100644
--- a/packages/i18n/src/locales/pt.json
+++ b/packages/i18n/src/locales/pt.json
@@ -255,6 +255,7 @@
"title": "Erro Interno do Servidor",
"description": "Esta página registou um erro irrecuperável."
},
+ "details": "Detalhes",
"backToHome": "Voltar à Página Inicial"
},
"download": {
diff --git a/packages/i18n/src/locales/ro.json b/packages/i18n/src/locales/ro.json
index 199b01ccf0b2d..05598f3091d7f 100644
--- a/packages/i18n/src/locales/ro.json
+++ b/packages/i18n/src/locales/ro.json
@@ -347,6 +347,7 @@
"title": "Eroare internă de server",
"description": "Această pagină a generat o eroare nerecuperabilă."
},
+ "details": "Detalii",
"backToHome": "Înapoi la prima pagină"
},
"download": {
diff --git a/packages/i18n/src/locales/ta.json b/packages/i18n/src/locales/ta.json
index 2b146ce2628b8..55f9f0847f263 100644
--- a/packages/i18n/src/locales/ta.json
+++ b/packages/i18n/src/locales/ta.json
@@ -366,6 +366,7 @@
"title": "உள் சேவையக பிழை",
"description": "இந்தப் பக்கத்தில் சரிசெய்ய முடியாத பிழை ஏற்பட்டது."
},
+ "details": "விவரங்கள்",
"backToHome": "முகப்பிற்கு திரும்பவும்"
},
"download": {
diff --git a/packages/i18n/src/locales/tr.json b/packages/i18n/src/locales/tr.json
index 41d9c8ff64e9f..5f92c805f991f 100644
--- a/packages/i18n/src/locales/tr.json
+++ b/packages/i18n/src/locales/tr.json
@@ -277,6 +277,7 @@
"title": "Dahili Sunucu Hatası",
"description": "Bu sayfa kurtarılamaz bir hata verdi."
},
+ "details": "Ayrıntılar",
"backToHome": "Ana Sayfaya Geri Dön"
},
"download": {
diff --git a/packages/i18n/src/locales/uk.json b/packages/i18n/src/locales/uk.json
index 055b9a43e5267..f9915340c4cc7 100644
--- a/packages/i18n/src/locales/uk.json
+++ b/packages/i18n/src/locales/uk.json
@@ -370,6 +370,7 @@
"title": "Внутрішня помилка сервера",
"description": "На цій сторінці виникла невиправна помилка."
},
+ "details": "Деталі",
"backToHome": "До головної"
},
"download": {
diff --git a/packages/i18n/src/locales/zh-cn.json b/packages/i18n/src/locales/zh-cn.json
index 02818c1415e50..bf4ccd99fe411 100644
--- a/packages/i18n/src/locales/zh-cn.json
+++ b/packages/i18n/src/locales/zh-cn.json
@@ -256,6 +256,7 @@
"title": "服务器内部错误",
"description": "该页面出现了不可恢复的错误。"
},
+ "details": "详细信息",
"backToHome": "回到首页"
},
"download": {
diff --git a/packages/i18n/src/locales/zh-tw.json b/packages/i18n/src/locales/zh-tw.json
index db3e3fd60164e..8413c437ff8d9 100644
--- a/packages/i18n/src/locales/zh-tw.json
+++ b/packages/i18n/src/locales/zh-tw.json
@@ -281,6 +281,7 @@
"title": "內部伺服器錯誤",
"description": "此頁面發生了無法恢復的錯誤。"
},
+ "details": "詳細資訊",
"backToHome": "回到首頁"
},
"download": {
From ea812c7da13c8ac44d7877aae8a40e2225afcc61 Mon Sep 17 00:00:00 2001
From: ZeroPointSix <229769386+ZeroPointSix@users.noreply.github.com>
Date: Sun, 22 Mar 2026 19:26:49 +0800
Subject: [PATCH 3/4] fix: keep new error i18n key in english only
---
packages/i18n/src/locales/es.json | 1 -
packages/i18n/src/locales/fr.json | 1 -
packages/i18n/src/locales/id.json | 1 -
packages/i18n/src/locales/ja.json | 1 -
packages/i18n/src/locales/ko.json | 1 -
packages/i18n/src/locales/pt-br.json | 1 -
packages/i18n/src/locales/pt.json | 1 -
packages/i18n/src/locales/ro.json | 1 -
packages/i18n/src/locales/ta.json | 1 -
packages/i18n/src/locales/tr.json | 1 -
packages/i18n/src/locales/uk.json | 1 -
packages/i18n/src/locales/zh-cn.json | 1 -
packages/i18n/src/locales/zh-tw.json | 1 -
13 files changed, 13 deletions(-)
diff --git a/packages/i18n/src/locales/es.json b/packages/i18n/src/locales/es.json
index 0e682fd41276c..dd15732fcecb6 100644
--- a/packages/i18n/src/locales/es.json
+++ b/packages/i18n/src/locales/es.json
@@ -275,7 +275,6 @@
"title": "Error interno del servidor",
"description": "Esta página ha generado un error no recuperable."
},
- "details": "Detalles",
"backToHome": "Volver al inicio"
},
"download": {
diff --git a/packages/i18n/src/locales/fr.json b/packages/i18n/src/locales/fr.json
index feb232f960240..851616fe1f311 100644
--- a/packages/i18n/src/locales/fr.json
+++ b/packages/i18n/src/locales/fr.json
@@ -370,7 +370,6 @@
"title": "Erreur interne du serveur",
"description": "Cette page a généré une erreur irrécupérable."
},
- "details": "Détails",
"backToHome": "Retourner à l'accueil"
},
"download": {
diff --git a/packages/i18n/src/locales/id.json b/packages/i18n/src/locales/id.json
index f5cce098415a4..e5ceffe8aa3a4 100644
--- a/packages/i18n/src/locales/id.json
+++ b/packages/i18n/src/locales/id.json
@@ -360,7 +360,6 @@
"title": "Kesalahan Server Internal",
"description": "Halaman ini mengalami error yang tidak dapat diperbaiki."
},
- "details": "Rincian",
"backToHome": "Kembali ke Beranda"
},
"download": {
diff --git a/packages/i18n/src/locales/ja.json b/packages/i18n/src/locales/ja.json
index d2dafc34014f9..6e6080d50e266 100644
--- a/packages/i18n/src/locales/ja.json
+++ b/packages/i18n/src/locales/ja.json
@@ -370,7 +370,6 @@
"title": "内部サーバーエラー",
"description": "このページで回復不可能なエラーが発生しました。"
},
- "details": "詳細",
"backToHome": "ホームに戻る"
},
"download": {
diff --git a/packages/i18n/src/locales/ko.json b/packages/i18n/src/locales/ko.json
index 810deee83d034..591a7b1422647 100644
--- a/packages/i18n/src/locales/ko.json
+++ b/packages/i18n/src/locales/ko.json
@@ -249,7 +249,6 @@
"title": "Internal Server Error",
"description": "이 페이지에서 복구할 수 없는 오류가 발생했습니다."
},
- "details": "세부정보",
"backToHome": "홈으로 돌아가기"
},
"download": {
diff --git a/packages/i18n/src/locales/pt-br.json b/packages/i18n/src/locales/pt-br.json
index 643069c8779cc..de37d98e132e7 100644
--- a/packages/i18n/src/locales/pt-br.json
+++ b/packages/i18n/src/locales/pt-br.json
@@ -366,7 +366,6 @@
"title": "Erro Interno do Servidor",
"description": "Esta página lançou um erro não recuperável."
},
- "details": "Detalhes",
"backToHome": "Voltar para a Página Inicial"
},
"download": {
diff --git a/packages/i18n/src/locales/pt.json b/packages/i18n/src/locales/pt.json
index cb5783e2f0c59..643d8e2def5c1 100644
--- a/packages/i18n/src/locales/pt.json
+++ b/packages/i18n/src/locales/pt.json
@@ -255,7 +255,6 @@
"title": "Erro Interno do Servidor",
"description": "Esta página registou um erro irrecuperável."
},
- "details": "Detalhes",
"backToHome": "Voltar à Página Inicial"
},
"download": {
diff --git a/packages/i18n/src/locales/ro.json b/packages/i18n/src/locales/ro.json
index 05598f3091d7f..199b01ccf0b2d 100644
--- a/packages/i18n/src/locales/ro.json
+++ b/packages/i18n/src/locales/ro.json
@@ -347,7 +347,6 @@
"title": "Eroare internă de server",
"description": "Această pagină a generat o eroare nerecuperabilă."
},
- "details": "Detalii",
"backToHome": "Înapoi la prima pagină"
},
"download": {
diff --git a/packages/i18n/src/locales/ta.json b/packages/i18n/src/locales/ta.json
index 55f9f0847f263..2b146ce2628b8 100644
--- a/packages/i18n/src/locales/ta.json
+++ b/packages/i18n/src/locales/ta.json
@@ -366,7 +366,6 @@
"title": "உள் சேவையக பிழை",
"description": "இந்தப் பக்கத்தில் சரிசெய்ய முடியாத பிழை ஏற்பட்டது."
},
- "details": "விவரங்கள்",
"backToHome": "முகப்பிற்கு திரும்பவும்"
},
"download": {
diff --git a/packages/i18n/src/locales/tr.json b/packages/i18n/src/locales/tr.json
index 5f92c805f991f..41d9c8ff64e9f 100644
--- a/packages/i18n/src/locales/tr.json
+++ b/packages/i18n/src/locales/tr.json
@@ -277,7 +277,6 @@
"title": "Dahili Sunucu Hatası",
"description": "Bu sayfa kurtarılamaz bir hata verdi."
},
- "details": "Ayrıntılar",
"backToHome": "Ana Sayfaya Geri Dön"
},
"download": {
diff --git a/packages/i18n/src/locales/uk.json b/packages/i18n/src/locales/uk.json
index f9915340c4cc7..055b9a43e5267 100644
--- a/packages/i18n/src/locales/uk.json
+++ b/packages/i18n/src/locales/uk.json
@@ -370,7 +370,6 @@
"title": "Внутрішня помилка сервера",
"description": "На цій сторінці виникла невиправна помилка."
},
- "details": "Деталі",
"backToHome": "До головної"
},
"download": {
diff --git a/packages/i18n/src/locales/zh-cn.json b/packages/i18n/src/locales/zh-cn.json
index bf4ccd99fe411..02818c1415e50 100644
--- a/packages/i18n/src/locales/zh-cn.json
+++ b/packages/i18n/src/locales/zh-cn.json
@@ -256,7 +256,6 @@
"title": "服务器内部错误",
"description": "该页面出现了不可恢复的错误。"
},
- "details": "详细信息",
"backToHome": "回到首页"
},
"download": {
diff --git a/packages/i18n/src/locales/zh-tw.json b/packages/i18n/src/locales/zh-tw.json
index 8413c437ff8d9..db3e3fd60164e 100644
--- a/packages/i18n/src/locales/zh-tw.json
+++ b/packages/i18n/src/locales/zh-tw.json
@@ -281,7 +281,6 @@
"title": "內部伺服器錯誤",
"description": "此頁面發生了無法恢復的錯誤。"
},
- "details": "詳細資訊",
"backToHome": "回到首頁"
},
"download": {
From 52cec4f0acbf23ae1efb7dee32a3f17ecb1f5020 Mon Sep 17 00:00:00 2001
From: ZeroPointSix <229769386+ZeroPointSix@users.noreply.github.com>
Date: Mon, 23 Mar 2026 10:28:43 +0800
Subject: [PATCH 4/4] fix(site): address error page review feedback
---
apps/site/app/[locale]/error.tsx | 14 ++++++--------
apps/site/next.config.mjs | 4 ----
2 files changed, 6 insertions(+), 12 deletions(-)
diff --git a/apps/site/app/[locale]/error.tsx b/apps/site/app/[locale]/error.tsx
index ce729f094e0cc..afeaeb7932b83 100644
--- a/apps/site/app/[locale]/error.tsx
+++ b/apps/site/app/[locale]/error.tsx
@@ -14,12 +14,7 @@ type ErrorPageProps = {
const ErrorPage: FC = ({ error }) => {
const t = useTranslations();
- const errorDetails = [
- error.message,
- error.digest && `digest: ${error.digest}`,
- ]
- .filter(Boolean)
- .join('\n');
+ const hasErrorDetails = Boolean(error.message || error.digest);
return (
@@ -33,14 +28,17 @@ const ErrorPage: FC = ({ error }) => {
{t('layouts.error.internalServerError.description')}
- {SHOW_ERROR_DETAILS && errorDetails && (
+ {SHOW_ERROR_DETAILS && hasErrorDetails && (
{t('layouts.error.details')}
- {errorDetails}
+ {error.message}
+ {error.digest
+ ? `${error.message ? '\n' : ''}digest: ${error.digest}`
+ : ''}
)}
diff --git a/apps/site/next.config.mjs b/apps/site/next.config.mjs
index 30447adaa239e..40d1f89e87cd3 100644
--- a/apps/site/next.config.mjs
+++ b/apps/site/next.config.mjs
@@ -29,10 +29,6 @@ const nextConfig = {
basePath: BASE_PATH,
// Vercel/Next.js Image Optimization Settings
images: getImagesConfig(),
- env: {
- NEXT_PUBLIC_VERCEL_ENV:
- process.env.NEXT_PUBLIC_VERCEL_ENV || process.env.VERCEL_ENV,
- },
serverExternalPackages: ['twoslash'],
outputFileTracingIncludes: {
// Twoslash needs TypeScript declarations to function, and, by default, Next.js