All Docs
FeaturesCalmony Sanctions MonitorUpdated March 12, 2026

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() from next/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:

  1. The record does not exist in the database.
  2. 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

ScenarioBefore v0.1.159After v0.1.159
Valid person IDRenders person recordRenders person record
Invalid / non-existent person IDBlank page (null data)Branded 404 page (HTTP 404)
Person ID belonging to another organisationBlank page (null data)Branded 404 page (HTTP 404)
Unmatched routeNext.js default 404Branded 404 page (HTTP 404)

Developer Notes

  • Always import notFound from '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.tsx file at the src/app root acts as the global fallback; you can also place a not-found.tsx inside a route segment folder for segment-specific 404 pages.