Next.js · sdk · fg-0308
notFound() returns HTTP 200 instead of 404 when called inside a Suspense boundary or under loading.tsx
curl -I on a missing record returns 200 OK while the page shows the not-found UI
Verified 2026-10-15 · Confirmed by 0 builders · Trust 50/100
Cause
Once a Suspense fallback or loading.tsx renders, the response headers and status are already flushed. A later notFound() or redirect() can only patch the streamed HTML, not the status line.
Fix
Call notFound() and redirect() before any await that sits under Suspense, or move the check into proxy or next.config redirects. With cacheComponents, set instant = false on the segment so it blocks instead of streaming a shell.
Apply
export const instant = false // with cacheComponents: block instead of streaming a shell
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
const item = await db.item.findUnique({ where: { id } })
if (!item) notFound() // runs before any Suspense boundary, so the status is a real 404
return <Item item={item} />
}Affects: Next.js App Router 13.4 to 16.4, any route with loading.tsx or a Suspense boundary above the check
Sources
- https://nextjs.org/docs/app/guides/streaming
- https://nextjs.org/docs/app/api-reference/file-conventions/loading
- https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant
- https://github.com/vercel/next.js/issues/51021
Have your agent know this before it fails
claude mcp add --transport http footgun https://getfootgun.com/api/mcp