# HP5008: A custom interaction failed

> HP5008 (interaction-failed) means an interaction from your config threw an error or caused a page error. Run it in a headed browser to see where it fails.

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

HP5008 (`interaction-failed`) means one of the custom interactions in your
config threw an error, or caused an uncaught error on the page while it ran.
Either the steps no longer match the page, or the page breaks when a user does
the same thing. Run the interaction in a headed browser to see where it fails.

| | |
| --- | --- |
| Code | `HP5008` |
| Name | `interaction-failed` |
| Default severity | Error |
| Group | Interaction during hydration |
| What it means | An interaction from the config threw an error or caused a page error. |

## What the HP5008 interaction failed finding means

Custom interactions are Playwright steps you add under `interactions` in the
config. Each one runs on the routes it matches, after hydration by default or
with the page's scripts held back (`when: "before-hydration"`). hydration-proof
reports the interaction by name:

```text
"continue to payment" failed: locator.click: Timeout 30000ms exceeded.
"continue to payment" caused an error on the page: TypeError: Cannot read properties of undefined (reading 'total')
```

Uncaught errors count only when they happen after the steps started. The page
errors are listed as evidence. It is an error, because either the test or the
page is broken.

## Likely causes

- A selector in the steps that no longer matches, after a change to the page.
- Steps that wait for something the page never shows.
- A `before-hydration` interaction that expects React to handle an event: while
  the scripts are held back, only plain HTML behavior works.
- A real bug: the page throws when a user does these steps.
- The page did not hydrate, so the steps never ran; the message says so.

## How to fix it

1. Run the interaction in a headed browser (`--headed`) to see where it
   fails; the command is below.
2. If a step fails, update the locator or the wait in the config. Locators by
   role or label (`getByRole`, `getByLabel`) survive markup changes better
   than CSS selectors.
3. If the page throws, fix the page: the finding's evidence has the error.
4. If a page did not hydrate, look at that page's own findings first, such as
   [HP9001](https://hydration.jscrate.dev/docs/issues/hp9001).

```bash
npx hydration-proof test --route /checkout --headed --workers 1
```

A step that finds its elements by label and role:

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

export default defineConfig({
  interactions: [
    {
      route: "/checkout",
      name: "continue to payment",
      steps: async ({ page }) => {
        await page.getByLabel("Email").fill("test@example.com");
        await page.getByRole("button", { name: "Continue" }).click();
        await page.getByText("Payment").waitFor();
      },
    },
  ],
});
```

[Interactions](https://hydration.jscrate.dev/docs/interactions) documents every field, including `when` and
`scenarios`.

## Related

- [Interaction and navigation checks](https://hydration.jscrate.dev/docs/interactions)
- [HP5001: a click before hydration was lost](https://hydration.jscrate.dev/docs/issues/hp5001)
- [HP2007: uncaught error while loading the page](https://hydration.jscrate.dev/docs/issues/hp2007)
- [Configuration reference](https://hydration.jscrate.dev/docs/configuration)
- [The CLI and its flags](https://hydration.jscrate.dev/docs/cli)
