# Troubleshooting

> hydration-proof troubleshooting: fix a missing browser, an app that won't start, skipped routes, failed logins, pages that never settle, missing source lines.

Source: https://hydration.jscrate.dev/docs/troubleshooting
Last updated: 2026-09-18

hydration-proof troubleshooting starts with `npx hydration-proof doctor`, which
checks Node.js, Playwright, the browsers, the config file and framework
detection in one go. The answers below cover the problems people hit most,
grouped by where they show up: installing and starting, signing in, routes,
findings, source locations and speed.

## Start hydration-proof troubleshooting with doctor

```bash
npx hydration-proof doctor
```

It prints one line per check, marked as passed, a warning or a failure:

| Check                     | What it tells you                                                                       |
| ------------------------- | --------------------------------------------------------------------------------------- |
| Node.js                   | The running version; 22.18 or newer is required                                         |
| Package manager, CI       | What was detected; CI changes defaults such as retries                                  |
| Playwright                | The `playwright-core` version in use and where it came from                             |
| chromium, firefox, webkit | Whether each browser is installed, with the command that installs it                    |
| Config                    | The config file it found, or the command that creates one                               |
| Framework                 | The [adapter](https://hydration.jscrate.dev/docs/adapters) that was detected, or "not detected (set server.command)" |
| Routes                    | How many static and dynamic routes the adapter discovered                               |

`doctor` exits with `0` when nothing failed and `2` when something did. A
missing Firefox or WebKit is only a warning; a missing Chromium is a failure.

## Installing and starting the app

### Why does it say "chromium is not installed"?

The browser for the Playwright version hydration-proof uses has not been
downloaded yet, and the run stops with exit code `4`. Install it:

```bash
npx hydration-proof install
npx hydration-proof install --with-deps   # on Linux CI, with system libraries
npx hydration-proof install firefox webkit
```

### Why does it say "Nothing answers at …"?

The app could not be built, started or reached, and the run exits with code
`3`. Check these in order:

1. **Run `server.command` on its own.** `{port}` in the command is replaced
   with the port hydration-proof chose, and the same port is in the `PORT`
   environment variable. A server that ignores both listens somewhere else.
2. **Give slow builds more time.** `server.timeout` is how long to wait for the
   app to answer, 120000 ms by default.
3. **With `--url`, start the app first.** Nothing is built or started when you
   pass a URL.

When a build or start command fails, the run prints the command and the end of
its output.

### What if hydration-proof does not know how to start the app?

No adapter matched, so there is no start command. Set `server.command` (and
`server.build`) in `hydration-proof.config.ts`, or pass `--url` to test an app
you started yourself. [Adapters](https://hydration.jscrate.dev/docs/adapters#custom-react-servers) shows a
config for a custom server.

### Why does Next.js development mode not load?

hydration-proof opens the development server on `localhost`, because Next.js
blocks development resources for other hosts. If you test a development server
through another host name, add that host to `allowedDevOrigins` in
`next.config`.

### Why does my TypeScript config fail to load?

Node.js loads `hydration-proof.config.ts` directly, without a compiler. Use
plain types (no `enum` or `namespace`), include the file extension in relative
imports (`./routes.ts`), or rename the file to `.mjs`.

## Signing in

### Why do signed-in pages end on the login page (HP9010)?

The scenario's `login` did not leave a working session, so the page redirected
and got an [HP9010](https://hydration.jscrate.dev/docs/issues/hp9010) warning.

1. Run with `--headed --workers 1` to watch the login.
2. Check that `login` waits for the page after signing in, for example with
   `page.waitForURL`.
3. Check that the session cookie is not limited to another domain.

If the route is supposed to redirect, set `expectRedirect` on it. See
[scenarios and sign-in](https://hydration.jscrate.dev/docs/scenarios).

### What does "The login of scenario … failed" or "The setup hook failed" mean?

The scenario's `login` function, or the `hooks.setup` function, threw an
error; its message follows. Both run after the app is up, with the same
`baseUrl` the pages use. Either failure stops the run with exit code `2`.

## Routes

### Why are some dynamic routes skipped?

A dynamic route is only tested with example values: from `routes.dynamic`, or
from pages the build pre-rendered. A route without values is skipped, with a
note that lists it. Add values, or list the URLs in a sitemap and turn on
`routes.sitemap`:

```ts title="hydration-proof.config.ts"
import { defineConfig } from "hydration-proof";

export default defineConfig({
  routes: { dynamic: { "/products/[id]": ["1", "42"] } },
});
```

### Why is a new page missing from the run?

Discovered routes are cached until the build or a route folder changes. Run
with `--no-cache` if a change was missed:

```bash
npx hydration-proof test --no-cache
```

## Findings

### How do I keep a difference that is intentional?

Mark the element with `data-hydration-proof-ignore`, add a selector to
`ignore.selectors`, or add an `ignore.issues` rule with a reason. For values
that differ on purpose, such as a live clock, render them after mount or put
`suppressHydrationWarning` on that element; hydration-proof then lists them as
info. [Ignoring findings](https://hydration.jscrate.dev/docs/ignoring) covers each option.

### What if a page keeps changing and never settles (HP9009)?

Pages with animations, polling or live data may never be quiet for the 400 ms
hydration-proof waits by default, and get an [HP9009](https://hydration.jscrate.dev/docs/issues/hp9009)
warning. Tell it what "ready" means for the page instead:

```ts title="hydration-proof.config.ts"
import { defineConfig } from "hydration-proof";

export default defineConfig({
  ready: {
    selector: "#app-ready", // an element that exists once the page is ready
    quietMs: 200, // or wait less time without changes
  },
});
```

`ready.function` takes a page function (as a string) that must return a truthy
value. A route object can have its own `ready` options.

### Why is a cause "not proven"?

[Probes](https://hydration.jscrate.dev/docs/probes) prove a cause when a finding changes with exactly one
factor. When the page renders differently on identical reloads, the value comes
from the server or an API, for example a counter or the current time on the
server, which the browser cannot control. Send the data the server rendered
with to the client.

### Why do navigation checks report differences?

Client navigation keeps state that a direct load does not have: module
variables, context in layouts, parallel route slots. If the difference is
intended (a modal from an intercepting route, a slot that keeps its page), add
an ignore rule for the route with `code: "HP5004"`. To start from another page,
set `checks.navigation.from`, or `navigateFrom` on the route.

### What is a lost click?

A server-rendered button is visible before React can handle its clicks, and a
click in that window does nothing ([HP5001](https://hydration.jscrate.dev/docs/issues/hp5001)). Show the
control as disabled or loading until the page is interactive, or make it work
without JavaScript. If that is acceptable for your app, ignore HP5001.

## Source locations

### Why is there no source location in production?

The issue's `sourceUnavailableReason` says why. Enable browser source maps
(`productionBrowserSourceMaps: true` in Next.js) to get the component, or run
with `--mode development` for the exact line. Elements rendered by Server
Components have no client code to point at.

### Why are source maps from my CDN not used?

Scripts and source maps are only fetched from the app's own origin, and the run
ends with a note that names the origin it refused. Add the CDN to
`sourceOrigins`. [Security](https://hydration.jscrate.dev/docs/security) explains why the restriction
exists.

### Why are component names single letters?

Production builds minify component names. Run with `--mode development` to see
the real names:

```bash
npx hydration-proof test --mode development
```

## Speed

### How do I make the matrix faster?

Every environment loads every route. Keep the default `strategy: "pairwise"`,
lower `matrix.max`, limit the matrix to some scenarios with
`matrix.scenarios`, or run quick local checks with `--no-matrix`. In CI, split
the pages across jobs with `--shard`. See the
[environment matrix](https://hydration.jscrate.dev/docs/environment-matrix).

### Why is network throttling different in Firefox and WebKit?

Only Chromium can throttle the network. In Firefox and WebKit, every request
except the page itself is delayed by the latency instead, which turns the HTTP
cache off. CPU slowdown is Chromium only.

## Related

- [CLI commands, flags and exit codes](https://hydration.jscrate.dev/docs/cli)
- [Configuration reference](https://hydration.jscrate.dev/docs/configuration)
- [Compatibility](https://hydration.jscrate.dev/docs/compatibility): supported Node.js, React and framework
  versions
- [Scenarios and sign-in](https://hydration.jscrate.dev/docs/scenarios)
- [Ignore known hydration mismatches](https://hydration.jscrate.dev/docs/ignoring)
