HP9009 (ready-timeout) means the page hydrated, but its ready conditions were
not met before the timeout: the DOM kept changing, or your ready selector or
function never passed. The final snapshot was taken anyway and may be early.
Set ready options that fit the page: a longer timeout, a shorter quiet
period or a selector.
| Code | HP9009 |
|---|---|
| Name | ready-timeout |
| Default severity | Warning |
| Group | Test run problems |
| What it means | The ready conditions (quiet DOM, selector, function) were not met before the timeout. |
What the HP9009 ready timeout finding means
After hydration, hydration-proof waits for the page to settle before it takes the stable snapshot. hydration-proof never waits for "network idle". A page counts as settled when:
- the element in
ready.selectorexists, if you set one; - the page function in
ready.functionreturns a truthy value, if you set one; - no DOM change or React commit happened for
ready.quietMs(400 ms by default).
All three must happen within ready.timeout (30 seconds by default). If they
do not, the snapshot is taken at the timeout and the page gets this warning:
The page kept changing until the timeout; the stable snapshot may be early.Likely causes
- Animations driven by JavaScript, carousels, tickers or a clock that updates
the DOM more often than
quietMs. - Polling or live data that re-renders the page every few hundred milliseconds.
- A
ready.selectorthat never appears, or aready.functionthat never returns true, for example after a change to the page. - A slow page that settles, but after the timeout.
How to fix it
The issue code's advice is to increase the timeout or provide ready.selector
for pages with continuous updates. In practice, pick by situation:
| Situation | Fix |
|---|---|
| The page settles, but slowly | Raise ready.timeout, or pass --timeout |
| The page updates all the time | Lower ready.quietMs for that route, below the interval of the updates |
| The content you care about arrives late | Set ready.selector or ready.function, so the snapshot waits for it |
| The selector or function never passes | Fix it, or remove it |
A route object can set its own ready options, so the rest of the app keeps
the defaults:
import { defineConfig } from "hydration-proof";
export default defineConfig({
routes: {
paths: [
"/",
{
path: "/live-scores",
ready: { quietMs: 150, selector: "#scoreboard" },
},
],
},
ready: { timeout: 45_000 },
});When the updates can be turned off for tests, a scenario can do it before any
page script runs, for example with localStorage or initScripts (see
scenarios). If none of this fits, ignore
code: "HP9009" for the route with a reason
(ignoring findings).