ERR-04: Proper 404 Handling for Dynamic Routes
ERR-04: Proper 404 Handling for Dynamic Routes
Released in: v0.1.159
Overview
Version 0.1.159 closes error-boundary control ERR-04 by introducing a branded 404 page and enforcing notFound() guards in all dynamic route server components. This ensures that invalid or unauthorised record lookups return a proper HTTP 404 response rather than silently rendering a blank page.
Why This Matters for Compliance Platforms
In a sanctions screening context, rendering a blank page when a person ID is not found leaks information: it implicitly confirms that a compliance record does not exist for that identifier. A proper 404 response is both better UX and the correct security posture — the platform should not distinguish between "record does not exist" and "record exists but does not belong to you" in its visible output.
What Changed
Global 404 Page (src/app/not-found.tsx)
A new top-level not-found.tsx file now provides a branded 404 page served by Next.js whenever:
- A route segment has no matching page file.
- A server component explicitly calls
notFound()fromnext/navigation.
This page is consistent with the rest of the application's design and gives users a clear, actionable message rather than an empty or broken layout.
Dynamic Route Guard — People (/dashboard/people/[id])
The page.tsx server component for individual person records now follows this pattern:
import { notFound } from 'next/navigation';
export default async function PersonPage({ params }: { params: { id: string } }) {
const person = await getPersonForUser(params.id, userId);
if (!person) {
notFound(); // triggers the branded 404 page
}
// render person record ...
}
The guard covers two cases:
- The record does not exist in the database.
- The record exists but does not belong to the authenticated user's organisation.
Both cases return the same 404 response — no information about record existence is leaked.
Dynamic Route Guard — Sanctions Changes
The same notFound() pattern is applied to the sanctions/changes dynamic routes, ensuring consistent behaviour across all record-level pages.
Behaviour Before and After
| Scenario | Before v0.1.159 | After v0.1.159 |
|---|---|---|
| Valid person ID | Renders person record | Renders person record |
| Invalid / non-existent person ID | Blank page (null data) | Branded 404 page (HTTP 404) |
| Person ID belonging to another organisation | Blank page (null data) | Branded 404 page (HTTP 404) |
| Unmatched route | Next.js default 404 | Branded 404 page (HTTP 404) |
Developer Notes
- Always import
notFoundfrom'next/navigation'(not'next/router'). notFound()throws internally — no return statement is needed after the call.- Apply this guard to every dynamic route server component that fetches a record by ID. Check both existence and ownership before rendering.
- The
not-found.tsxfile at thesrc/approot acts as the global fallback; you can also place anot-found.tsxinside a route segment folder for segment-specific 404 pages.