diff --git a/apps/web/app/[locale]/(public)/agents/[id]/error.tsx b/apps/web/app/[locale]/(public)/agents/[id]/error.tsx
new file mode 100644
index 0000000..0b1893b
--- /dev/null
+++ b/apps/web/app/[locale]/(public)/agents/[id]/error.tsx
@@ -0,0 +1,60 @@
+'use client';
+
+import { useEffect } from 'react';
+
+export default function AgentProfileError({
+ error,
+ reset,
+}: {
+ error: Error & { digest?: string };
+ reset: () => void;
+}) {
+ useEffect(() => {
+ console.error('Agent profile error:', error);
+ }, [error]);
+
+ return (
+
+
+
+
+
Không thể tải hồ sơ môi giới
+
+ Đã xảy ra lỗi khi tải thông tin môi giới. Vui lòng thử lại.
+
+ {error.digest && (
+
Mã lỗi: {error.digest}
+ )}
+
+
+
+
+ );
+}
diff --git a/apps/web/app/[locale]/(public)/agents/error.tsx b/apps/web/app/[locale]/(public)/agents/error.tsx
new file mode 100644
index 0000000..e906e43
--- /dev/null
+++ b/apps/web/app/[locale]/(public)/agents/error.tsx
@@ -0,0 +1,60 @@
+'use client';
+
+import { useEffect } from 'react';
+
+export default function AgentsError({
+ error,
+ reset,
+}: {
+ error: Error & { digest?: string };
+ reset: () => void;
+}) {
+ useEffect(() => {
+ console.error('Agents page error:', error);
+ }, [error]);
+
+ return (
+
+
+
+
+
Không thể tải thông tin môi giới
+
+ Đã xảy ra lỗi khi tải danh sách môi giới. Vui lòng thử lại.
+
+ {error.digest && (
+
Mã lỗi: {error.digest}
+ )}
+
+
+
+
+ );
+}
diff --git a/apps/web/app/[locale]/(public)/du-an/[slug]/error.tsx b/apps/web/app/[locale]/(public)/du-an/[slug]/error.tsx
new file mode 100644
index 0000000..7e19c37
--- /dev/null
+++ b/apps/web/app/[locale]/(public)/du-an/[slug]/error.tsx
@@ -0,0 +1,60 @@
+'use client';
+
+import { useEffect } from 'react';
+
+export default function ProjectDetailError({
+ error,
+ reset,
+}: {
+ error: Error & { digest?: string };
+ reset: () => void;
+}) {
+ useEffect(() => {
+ console.error('Project detail error:', error);
+ }, [error]);
+
+ return (
+
+
+
+
+
Không thể tải thông tin dự án
+
+ Đã xảy ra lỗi khi tải chi tiết dự án. Vui lòng thử lại.
+
+ {error.digest && (
+
Mã lỗi: {error.digest}
+ )}
+
+
+
+
+ );
+}
diff --git a/apps/web/app/[locale]/(public)/du-an/error.tsx b/apps/web/app/[locale]/(public)/du-an/error.tsx
new file mode 100644
index 0000000..b0fddf3
--- /dev/null
+++ b/apps/web/app/[locale]/(public)/du-an/error.tsx
@@ -0,0 +1,60 @@
+'use client';
+
+import { useEffect } from 'react';
+
+export default function ProjectsError({
+ error,
+ reset,
+}: {
+ error: Error & { digest?: string };
+ reset: () => void;
+}) {
+ useEffect(() => {
+ console.error('Projects (du-an) error:', error);
+ }, [error]);
+
+ return (
+
+
+
+
+
Không thể tải danh sách dự án
+
+ Đã xảy ra lỗi khi tải dự án bất động sản. Vui lòng thử lại.
+
+ {error.digest && (
+
Mã lỗi: {error.digest}
+ )}
+
+
+
+
+ );
+}
diff --git a/apps/web/app/[locale]/(public)/error.tsx b/apps/web/app/[locale]/(public)/error.tsx
new file mode 100644
index 0000000..96b34a0
--- /dev/null
+++ b/apps/web/app/[locale]/(public)/error.tsx
@@ -0,0 +1,60 @@
+'use client';
+
+import { useEffect } from 'react';
+
+export default function PublicError({
+ error,
+ reset,
+}: {
+ error: Error & { digest?: string };
+ reset: () => void;
+}) {
+ useEffect(() => {
+ console.error('Public page error:', error);
+ }, [error]);
+
+ return (
+
+
+
+
+
Không thể tải trang
+
+ Đã xảy ra lỗi khi tải nội dung. Vui lòng thử lại.
+
+ {error.digest && (
+
Mã lỗi: {error.digest}
+ )}
+
+
+
+
+ );
+}
diff --git a/apps/web/app/[locale]/(public)/khu-cong-nghiep/[slug]/error.tsx b/apps/web/app/[locale]/(public)/khu-cong-nghiep/[slug]/error.tsx
new file mode 100644
index 0000000..ea33115
--- /dev/null
+++ b/apps/web/app/[locale]/(public)/khu-cong-nghiep/[slug]/error.tsx
@@ -0,0 +1,60 @@
+'use client';
+
+import { useEffect } from 'react';
+
+export default function IndustrialParkDetailError({
+ error,
+ reset,
+}: {
+ error: Error & { digest?: string };
+ reset: () => void;
+}) {
+ useEffect(() => {
+ console.error('Industrial park detail error:', error);
+ }, [error]);
+
+ return (
+
+
+
+
+
Không thể tải chi tiết khu công nghiệp
+
+ Đã xảy ra lỗi khi tải thông tin khu công nghiệp. Vui lòng thử lại.
+
+ {error.digest && (
+
Mã lỗi: {error.digest}
+ )}
+
+
+
+
+ );
+}
diff --git a/apps/web/app/[locale]/(public)/khu-cong-nghiep/error.tsx b/apps/web/app/[locale]/(public)/khu-cong-nghiep/error.tsx
new file mode 100644
index 0000000..27a3067
--- /dev/null
+++ b/apps/web/app/[locale]/(public)/khu-cong-nghiep/error.tsx
@@ -0,0 +1,60 @@
+'use client';
+
+import { useEffect } from 'react';
+
+export default function IndustrialParksError({
+ error,
+ reset,
+}: {
+ error: Error & { digest?: string };
+ reset: () => void;
+}) {
+ useEffect(() => {
+ console.error('Industrial parks error:', error);
+ }, [error]);
+
+ return (
+
+
+
+
+
Không thể tải thông tin khu công nghiệp
+
+ Đã xảy ra lỗi khi tải dữ liệu khu công nghiệp. Vui lòng thử lại.
+
+ {error.digest && (
+
Mã lỗi: {error.digest}
+ )}
+
+
+
+
+ );
+}
diff --git a/apps/web/app/[locale]/(public)/listings/[id]/error.tsx b/apps/web/app/[locale]/(public)/listings/[id]/error.tsx
new file mode 100644
index 0000000..f13a18f
--- /dev/null
+++ b/apps/web/app/[locale]/(public)/listings/[id]/error.tsx
@@ -0,0 +1,60 @@
+'use client';
+
+import { useEffect } from 'react';
+
+export default function ListingDetailError({
+ error,
+ reset,
+}: {
+ error: Error & { digest?: string };
+ reset: () => void;
+}) {
+ useEffect(() => {
+ console.error('Listing detail error:', error);
+ }, [error]);
+
+ return (
+
+
+
+
+
Không thể tải thông tin bất động sản
+
+ Đã xảy ra lỗi khi tải chi tiết bất động sản. Vui lòng thử lại.
+
+ {error.digest && (
+
Mã lỗi: {error.digest}
+ )}
+
+
+
+
+ );
+}
diff --git a/apps/web/app/[locale]/(public)/listings/error.tsx b/apps/web/app/[locale]/(public)/listings/error.tsx
new file mode 100644
index 0000000..e657e70
--- /dev/null
+++ b/apps/web/app/[locale]/(public)/listings/error.tsx
@@ -0,0 +1,60 @@
+'use client';
+
+import { useEffect } from 'react';
+
+export default function ListingsError({
+ error,
+ reset,
+}: {
+ error: Error & { digest?: string };
+ reset: () => void;
+}) {
+ useEffect(() => {
+ console.error('Listings error:', error);
+ }, [error]);
+
+ return (
+
+
+
+
+
Không thể tải danh sách bất động sản
+
+ Đã xảy ra lỗi khi tải danh sách. Vui lòng thử lại.
+
+ {error.digest && (
+
Mã lỗi: {error.digest}
+ )}
+
+
+
+
+ );
+}
diff --git a/apps/web/app/[locale]/(public)/payment/error.tsx b/apps/web/app/[locale]/(public)/payment/error.tsx
new file mode 100644
index 0000000..5cc1e85
--- /dev/null
+++ b/apps/web/app/[locale]/(public)/payment/error.tsx
@@ -0,0 +1,60 @@
+'use client';
+
+import { useEffect } from 'react';
+
+export default function PaymentError({
+ error,
+ reset,
+}: {
+ error: Error & { digest?: string };
+ reset: () => void;
+}) {
+ useEffect(() => {
+ console.error('Payment page error:', error);
+ }, [error]);
+
+ return (
+
+
+
+
+
Lỗi thanh toán
+
+ Đã xảy ra lỗi trong quá trình thanh toán. Vui lòng thử lại hoặc liên hệ hỗ trợ.
+
+ {error.digest && (
+
Mã lỗi: {error.digest}
+ )}
+
+
+
+
+ );
+}
diff --git a/apps/web/app/[locale]/auth/callback/error.tsx b/apps/web/app/[locale]/auth/callback/error.tsx
new file mode 100644
index 0000000..4d56b1d
--- /dev/null
+++ b/apps/web/app/[locale]/auth/callback/error.tsx
@@ -0,0 +1,58 @@
+'use client';
+
+import { useEffect } from 'react';
+
+export default function AuthCallbackError({
+ error,
+ reset,
+}: {
+ error: Error & { digest?: string };
+ reset: () => void;
+}) {
+ useEffect(() => {
+ console.error('Auth callback error:', error);
+ }, [error]);
+
+ return (
+
+
+
+
Lỗi đăng nhập
+
+ Không thể hoàn tất quá trình đăng nhập. Vui lòng thử lại.
+
+ {error.digest && (
+
Mã lỗi: {error.digest}
+ )}
+
+
+
+ );
+}
diff --git a/apps/web/app/global-error.tsx b/apps/web/app/global-error.tsx
new file mode 100644
index 0000000..65948fb
--- /dev/null
+++ b/apps/web/app/global-error.tsx
@@ -0,0 +1,126 @@
+'use client';
+
+import { useEffect } from 'react';
+
+export default function GlobalError({
+ error,
+ reset,
+}: {
+ error: Error & { digest?: string };
+ reset: () => void;
+}) {
+ useEffect(() => {
+ console.error('Global error:', error);
+ }, [error]);
+
+ return (
+
+
+
+
+
+
+ Đã xảy ra lỗi nghiêm trọng
+
+
+ Ứng dụng gặp sự cố không mong muốn. Vui lòng tải lại trang.
+
+ {error.digest && (
+
+ Mã lỗi: {error.digest}
+
+ )}
+
+
+
+
+
+ );
+}
diff --git a/apps/web/docs/error-boundary-coverage.md b/apps/web/docs/error-boundary-coverage.md
new file mode 100644
index 0000000..3da60d1
--- /dev/null
+++ b/apps/web/docs/error-boundary-coverage.md
@@ -0,0 +1,93 @@
+# Error Boundary Coverage
+
+Audited: 2026-04-24 | Issue: [GOO-115](/GOO/issues/GOO-115)
+
+## Summary
+
+| Route group | Segment | `error.tsx` | Notes |
+|---|---|:---:|---|
+| root | `app/` | ✅ | `app/error.tsx` |
+| root global | `app/` | ✅ | `app/global-error.tsx` — added GOO-115 |
+| locale root | `app/[locale]/` | ✅ | `app/[locale]/error.tsx` |
+| **(admin)** group | `(admin)/` | ✅ | covers all admin sub-routes |
+| **(auth)** group | `(auth)/` | ✅ | covers login / register |
+| auth callback | `[locale]/auth/callback/` | ✅ | added GOO-115 |
+| **(dashboard)** group | `(dashboard)/` | ✅ | covers all dashboard sub-routes |
+| **(public)** group | `(public)/` | ✅ | added GOO-115 — fallback for uncovered public routes |
+| public — search | `(public)/search/` | ✅ | existed pre-audit |
+| public — listings | `(public)/listings/` | ✅ | added GOO-115 |
+| public — listings detail | `(public)/listings/[id]/` | ✅ | added GOO-115 |
+| public — du-an | `(public)/du-an/` | ✅ | added GOO-115 |
+| public — du-an detail | `(public)/du-an/[slug]/` | ✅ | added GOO-115 |
+| public — khu-cong-nghiep | `(public)/khu-cong-nghiep/` | ✅ | added GOO-115 |
+| public — khu-cong-nghiep detail | `(public)/khu-cong-nghiep/[slug]/` | ✅ | added GOO-115 |
+| public — agents | `(public)/agents/` | ✅ | added GOO-115 |
+| public — agent profile | `(public)/agents/[id]/` | ✅ | added GOO-115 |
+| public — payment | `(public)/payment/` | ✅ | added GOO-115 |
+
+## Routes covered by group boundary (no per-route file needed)
+
+These routes fall under a group-level `error.tsx` that handles them:
+
+| Route | Covered by |
+|---|---|
+| `(public)/bao-cao/` | `(public)/error.tsx` |
+| `(public)/bao-cao/[id]/` | `(public)/error.tsx` |
+| `(public)/bao-cao/tao-moi/` | `(public)/error.tsx` |
+| `(public)/chuyen-nhuong/` | `(public)/error.tsx` |
+| `(public)/chuyen-nhuong/[id]/` | `(public)/error.tsx` |
+| `(public)/chuyen-nhuong/dang-tin/` | `(public)/error.tsx` |
+| `(public)/compare/` | `(public)/error.tsx` |
+| `(public)/design-system/` | `(public)/error.tsx` |
+| `(public)/khu-cong-nghiep/cho-thue/` | `(public)/khu-cong-nghiep/error.tsx` |
+| `(public)/khu-cong-nghiep/so-sanh/` | `(public)/khu-cong-nghiep/error.tsx` |
+| `(public)/payment/return/` | `(public)/payment/error.tsx` |
+| `(public)/pricing/` | `(public)/error.tsx` |
+| `(admin)/admin/accounts/developers/` | `(admin)/error.tsx` |
+| `(admin)/admin/accounts/park-operators/` | `(admin)/error.tsx` |
+| `(admin)/admin/audit-log/` | `(admin)/error.tsx` |
+| `(admin)/admin/kyc/` | `(admin)/error.tsx` |
+| `(admin)/admin/moderation/` | `(admin)/error.tsx` |
+| `(admin)/admin/settings/ai/` | `(admin)/error.tsx` |
+| `(admin)/admin/users/` | `(admin)/error.tsx` |
+| `(auth)/login/` | `(auth)/error.tsx` |
+| `(auth)/register/` | `(auth)/error.tsx` |
+| `(dashboard)/dashboard/kyc/` | `(dashboard)/error.tsx` |
+| `(dashboard)/dashboard/payments/` | `(dashboard)/error.tsx` |
+| `(dashboard)/dashboard/profile/` | `(dashboard)/error.tsx` |
+| `(dashboard)/dashboard/reports/` | `(dashboard)/error.tsx` |
+| `(dashboard)/dashboard/reports/[id]/` | `(dashboard)/error.tsx` |
+| `(dashboard)/dashboard/reports/new/` | `(dashboard)/error.tsx` |
+| `(dashboard)/dashboard/saved-searches/` | `(dashboard)/error.tsx` |
+| `(dashboard)/dashboard/subscription/` | `(dashboard)/error.tsx` |
+| `(dashboard)/dashboard/valuation/` | `(dashboard)/error.tsx` |
+| `(dashboard)/dev/tokens/` | `(dashboard)/error.tsx` |
+| `(dashboard)/industrial-parks/` | `(dashboard)/error.tsx` |
+| `(dashboard)/industrial-parks/[id]/edit/` | `(dashboard)/error.tsx` |
+| `(dashboard)/industrial-parks/new/` | `(dashboard)/error.tsx` |
+| `(dashboard)/inquiries/` | `(dashboard)/error.tsx` |
+| `(dashboard)/leads/` | `(dashboard)/error.tsx` |
+| `(dashboard)/my-listings/` | `(dashboard)/error.tsx` |
+| `(dashboard)/my-listings/[id]/edit/` | `(dashboard)/error.tsx` |
+| `(dashboard)/my-listings/new/` | `(dashboard)/error.tsx` |
+| `(dashboard)/projects/` | `(dashboard)/error.tsx` |
+| `(dashboard)/projects/[id]/edit/` | `(dashboard)/error.tsx` |
+| `(dashboard)/projects/new/` | `(dashboard)/error.tsx` |
+| `(dashboard)/analytics/` | `(dashboard)/error.tsx` |
+| `[locale]/auth/callback/google/` | `auth/callback/error.tsx` |
+| `[locale]/auth/callback/zalo/` | `auth/callback/error.tsx` |
+
+## Files added in GOO-115
+
+- `apps/web/app/global-error.tsx`
+- `apps/web/app/[locale]/(public)/error.tsx`
+- `apps/web/app/[locale]/(public)/listings/error.tsx`
+- `apps/web/app/[locale]/(public)/listings/[id]/error.tsx`
+- `apps/web/app/[locale]/(public)/du-an/error.tsx`
+- `apps/web/app/[locale]/(public)/du-an/[slug]/error.tsx`
+- `apps/web/app/[locale]/(public)/khu-cong-nghiep/error.tsx`
+- `apps/web/app/[locale]/(public)/khu-cong-nghiep/[slug]/error.tsx`
+- `apps/web/app/[locale]/(public)/agents/error.tsx`
+- `apps/web/app/[locale]/(public)/agents/[id]/error.tsx`
+- `apps/web/app/[locale]/(public)/payment/error.tsx`
+- `apps/web/app/[locale]/auth/callback/error.tsx`