Next.js 'Error Occurred in the Server Components Render': Fix
Production shows 'An error occurred in the Server Components render. The specific message is omitted' and no clue why? Use the digest to find the real error, then fix it and show users a safe fallback.
The message "An error occurred in the Server Components render. The specific message is omitted in production builds" means a Server Component threw an error on the server, and Next.js hid the details on purpose. The real error is in your server logs, tagged with the same digest value the browser received. Search your logs for that digest, fix the root cause, and add an error.tsx so users see a safe fallback instead of a blank page.
The full text usually looks like this in the browser console or on the page:
Error: An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.
It never appears in next dev, because dev shows the full error in an overlay. In production the browser only gets a generic message and a short hash. This guide shows how to turn that hash into the real error.
Step 1: Understand Why the Message Is Hidden
Server Components run on your server. Their errors can contain database hostnames, file paths, SQL, or API keys pulled from a failed request. If Next.js sent that text to the browser, anyone could read it.
So in production builds, the Next.js error handling docs say errors forwarded from Server Components are stripped of specific details. The client gets a generic message plus a digest. The docs for error.js say the digest can be used to match the corresponding error in server-side logs.
The hidden message is not a bug. It is a safeguard. Your job is to read the server side.
Step 2: Grab the Digest
The digest is a short string, a few digits long. You can get it from three places:
- The browser console, on the error object (
error.digest). - Your
error.tsxcomponent, which receives it as a prop. - A user report, if you show the digest in your error screen (see Step 5).
Copy it. You will search for it next.
Step 3: Find the Real Error in Server Logs
Next.js writes the full message and stack trace to the server output, tagged with the same digest. A DEV Community guide on this describes the same flow.
Where to look depends on where you host:
- Vercel: open the project, go to Logs, filter to errors, and search the digest. For older errors you need a log drain or a longer retention plan. See Vercel log drains.
- Docker or a VM: search the container output, for example
docker logs <container> 2>&1 | grep "<digest>". - Any host: if the digest shows nothing, widen the time window. Logs from a different instance or an older deploy may hold it.
The stack trace usually points at one of a handful of causes. The most common ones:
- A database or API call failed (timeout, wrong credentials, a missing table).
- An environment variable is missing in production. See env variables undefined in production.
- Code assumed data existed and read a property of
undefined. See TypeError: cannot read properties of undefined. - A route that reads
cookies()orheaders()was built statically. See Dynamic Server Usage.
Step 4: Log Every Server Error Yourself With onRequestError
Do not wait for a user to report a digest. Next.js 15 added an onRequestError hook in instrumentation.ts. It runs when the server throws during a request and gives you the error, the request, and some context. The instrumentation docs show the pattern.
// instrumentation.ts (project root, or src/)
import type { Instrumentation } from 'next'
export const onRequestError: Instrumentation.onRequestError = async (
err,
request,
context
) => {
const e = err as Error & { digest?: string }
await fetch(process.env.ERROR_LOG_URL!, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
message: e.message,
stack: e.stack,
digest: e.digest,
path: request.path,
routeType: context.routeType,
}),
})
}
Two cautions from the docs. First, the error passed in may not be the exact thing you threw, because React can process it while rendering a Server Component. The digest is the reliable way to identify it. Second, await any async work inside the hook so it finishes before the function ends. Check the docs for your installed version, since this API is newer than the rest of the App Router.
If you use an error tracker such as PostHog, its Next.js setup also uses this hook.
Step 5: Add error.tsx So Users See a Fallback
A thrown Server Component error with no boundary gives users a broken page. Add an error.tsx next to the route. Per the Next.js docs, it catches errors from Server Components in its own segment and the segments below it, and it must be a Client Component.
// app/dashboard/error.tsx
'use client'
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<div>
<h2>Something went wrong</h2>
<p>Please try again. If it keeps happening, send us this code:</p>
<code>{error.digest}</code>
<button onClick={() => reset()}>Try again</button>
</div>
)
}
Showing the digest turns every user report into a log search key. reset() re-renders the segment, which helps with short-lived failures such as a flaky upstream call.
Two limits to know. An error.tsx does not catch errors thrown in the layout.tsx of the same segment. Errors in the root layout need app/global-error.tsx, which must render its own <html> and <body> and only takes effect in production. For more patterns, see React error boundaries in production.
Step 6: Handle Expected Failures as Return Values
Do not throw for things you expect, such as "email already taken" or "card declined". Thrown errors get their message hidden in production, which is why a custom message you saw locally turns generic after deploy. A Next.js forum answer gives the same advice: return the error as a value.
'use server'
export async function signUp(formData: FormData) {
const email = String(formData.get('email'))
const exists = await userExists(email)
if (exists) {
return { ok: false, message: 'That email is already registered.' }
}
await createUser(email)
return { ok: true }
}
Throw only for real bugs. Those should reach your logs and your error boundary. The full pattern is in Server Actions error handling.
Step 7: Rule Out a Platform Problem
If the error appears on many pages at once, and you shipped nothing, check your host's status page. Several reports in the Vercel community forum show this error during platform-side problems, not app bugs. A quick status check saves an hour of searching your own code.
Also test with a production build locally. next build && next start is much closer to production than next dev, and it often reproduces the error with your own console output.
How Relia Helps
Relia watches your users, APIs, database and servers together. When a Server Component starts failing after a release, it joins the failing requests, the deploy and any slow query into one incident, explained in plain words. It shows which users hit the error. When there is a stack trace, it gives a fix plan with the file and line, and after you deploy it checks whether the errors stopped. Setup for Node.js and browser apps is npx relia-sdk@latest init, and there is a Free plan.
FAQ
What is the digest in a Next.js error?
A short hash Next.js attaches to a server error. The browser gets the digest, and your server log holds the full error with the same digest. It lets you match a user's error to a log line without leaking details.
Why does the error only happen in production?
Dev mode shows full errors in an overlay. Production builds hide them. The underlying bug often only shows in production too, because of missing environment variables, a different database, or real data.
Can I show the real error message to users?
You can for errors you return as values, because those are data you chose to send. Do not expose raw thrown errors. They may leak secrets, paths or queries.
Does error.tsx fix the error?
No. It only controls what the user sees. You still need the digest and logs to find and fix the cause.
Why is my digest not in the Vercel logs?
The log window may be too short, or the error came from another deployment or region. Widen the time range, or use a log drain so older errors are kept.