GioJSdocs
On this page

error.tsx

The error UI for a folder and everything below it: the 500 page when a render fails on the server, and an error boundary in the browser.

app/dashboard/error.tsx
import React from 'react';
import type { ErrorPageProps } from '@gio.js/core';

export default function DashboardError({ error, reset }: ErrorPageProps) {
  return (
    <div role="alert">
      <h2>The dashboard could not be shown</h2>
      {error.digest !== undefined && <p>Reference: {error.digest}</p>}
      {reset !== undefined && <button onClick={reset}>Try again</button>}
    </div>
  );
}

Reference

File name and location

error.tsx, error.jsx or error.js, in app/ or any folder below it (route groups and dynamic folders included, private folders never). The nearest one at or above the page that failed is used.

Props

PropTypeDefaultDescription
error.message (required)string-The error's message in development. In production it is always generic: Internal Server Error for a server failure, Application Error for one in the browser, and Not Found for a notFound() call caught in the browser.
error.digeststring | undefined-A short reference the server logged the real error under (for example 140011e66130). Show it, so users can quote it in a report. Errors that only happened in the browser have none.
reset(() => void) | undefined-Renders the failed segment again. Only present when the error was caught in the browser: a server-rendered 500 page is static HTML.

Type them with ErrorPageProps from @gio.js/core.

Module exports

default (required), and optionally metadata or generateMetadata for the head of the server-rendered 500 page. If generateMetadata throws (often the same outage the page reports), the static metadata exports are used instead.

Behavior

On the server. When getServerSideProps, the page or a layout throws before the response has started:

  • The response is 500 with the nearest error.tsx, rendered inside the layouts of its own folder (not those of the page that failed). It is never cached.
  • The message and stack go to the log under the digest; the page gets only the digest in production.
  • If that error.tsx cannot render either (its own layouts throw), the one in the folder above is tried, and so on. With none left, the server answers with its built-in error page.
  • The 500 page is server-only HTML: it does not hydrate, so it has no reset, and links in it are plain links.

In the browser. Every error.tsx is also bundled into the pages below its folder as a React error boundary:

  • A render error after hydration (or during a soft navigation) replaces only that folder's segment with the error UI; layouts above it stay.
  • reset() clears the error and renders the segment again. Navigating to another URL, or only to another query string, clears it too.
  • React error boundaries do not catch errors in event handlers or in async code outside rendering: handle those where they happen.

Where the boundary sits

text
<Layout>               app/dashboard/layout.tsx  - NOT caught by dashboard/error.tsx
  <ErrorBoundary>      app/dashboard/error.tsx
    <Suspense>         app/dashboard/loading.tsx
      <Page />         app/dashboard/page.tsx    - caught

So an error.tsx never handles an error thrown by the layout in its own folder. Put an error.tsx in the parent folder for that.

Examples

A root error page

app/error.tsx
import React from 'react';
import type { ErrorPageProps } from '@gio.js/core';

export default function ErrorPage({ error, reset }: ErrorPageProps) {
  return (
    <main className="status">
      <h1>Something went wrong</h1>
      {process.env.NODE_ENV === 'development' && <pre>{error.message}</pre>}
      {error.digest !== undefined && (
        <p>
          Error reference: <code>{error.digest}</code>
        </p>
      )}
      {reset !== undefined ? (
        <button type="button" onClick={reset}>Try again</button>
      ) : (
        <a href="/">Go home</a>
      )}
    </main>
  );
}

Find the error in the logs

The digest a user reports matches the digest field of the log line written when the render failed:

text
{"ts":"2026-10-07T15:19:31.677Z","level":"error","msg":"ssr render failed","requestId":"b0533d97-...","path":"/dashboard/boom","digest":"140011e66130","error":"boom from gssp","stack":"Error: boom from gssp\n    at getServerSideProps (app/dashboard/[id]/page.tsx:6:39) ..."}

Good to know

  • error.tsx runs in the browser, so it must be browser-safe. One that imports server-only code leaves the pages below it without hydration, and the build error names the file. No 'use client' directive is needed.
  • After a streamed page has suspended inside a loading.tsx boundary, the 200 is already sent. A later error is shown by the browser's error boundary and the status stays 200.
  • Route handlers (route.ts) never use error.tsx: they answer a JSON 500 with a digest.
  • Hiding the error details in production is fixed: it cannot be turned off, because messages and stacks can contain secrets, SQL or file paths. Read them in the log.
  • There is no global-error.tsx. An error in the root layout itself gets the built-in error page, since every error.tsx renders inside it.
  • gio export reports a page that fails to render, with its digest, instead of writing the error page in its place.

Version history

VersionChanges
v0.1.0-beta.8Works in any folder, with the nearest one applying. Also a client error boundary with reset. Production shows only a digest: the props are { error: { message, digest } }. ErrorPageProps type.
v0.1.0-beta.5Introduced for app/error.tsx: the server renders it with status 500 when a page throws.