All docs
Recipes6 min readUpdated

Troubleshooting UI Verify

Fixes for the common UI Verify setup problems: no check on your PR, an empty archive or upload, phantom diffs after branching, and auth failures on upload.

Most first-run problems fail quietly - the upload succeeds and something downstream is just missing. Here are the ones we see most, each with the reason and the fix.

Why does nothing show up on my PR when the upload succeeds?

The GitHub App is not installed, not pointed at this repo, or not connected to this project. Uploads work with only the API key, but the check and PR comment need the App, and they only post through the installation connected to the project, never one found from the repo name. Install or connect it from your project settings and select the repo - see the GitHub App step of the Storybook quickstart.

Why does installing the GitHub App say it couldn't connect?

UI Verify will not connect a GitHub App installation it cannot safely match to your team, for example one another team already connected, or an older installation it has no record of you making. You see a notice that it couldn't connect. Email support@uiverify.ai with your project and we will look into it.

Why did my Playwright build upload nothing?

Installing @uiverify/playwright is not enough - you have to swap the import in your tests to import { test, expect } from "@uiverify/playwright". Without the swap, nothing is written to ./uiverify-archive and the upload has an empty directory. See step 2 of the Playwright quickstart.

Why did a Vitest story archive blank or empty?

render() from vitest-browser-react is async, so a takeSnapshot() or end-of-test capture that runs before it commits archives an empty body over your stylesheet - a blank screenshot that then becomes a blank baseline. Await the render: await render(<Component />). See step 2 of the Vitest quickstart.

Why does every PR show changes I didn't make?

Your CI is checking out a shallow clone, so UI Verify cannot resolve the right baseline from your branch history. Add fetch-depth: 0 to your actions/checkout step. If only one or two stories look off, they usually render something your diff changed indirectly - see Baselines and branches.

Why does a clock, ad, or live feed show as changed on every build?

That region renders different content each run, and the screenshot catches it faithfully. Pin it at the source when you can, with a frozen clock or fixture data: see Fix flaky visual tests. For content you cannot pin, like an ad slot, a map or a live feed, add data-uiverify-ignore to the element and UI Verify masks it in every capture. See Ignore regions.

Why does playwright test fail on CI with a missing browser?

A clean CI runner has no browsers installed. Add npx playwright install --with-deps before you run your tests, as in the Playwright quickstart CI workflow.

Why does playwright install fail with "Playwright does not support chromium on ubuntu26.04-x64"?

Your CI runner is on a newer OS version than your Playwright release supports. Each Playwright release installs browsers only on the OS versions it knows, so when a floating image moves on (GitHub's ubuntu-latest moving to Ubuntu 26.04, a :latest Docker base, a hosted-runner upgrade on GitLab or CircleCI), a workflow that passed yesterday can stop at npx playwright install --with-deps and never reach the upload. Ubuntu 26.04 needs Playwright 1.61 or newer. Upgrade playwright or @playwright/test and commit the updated lockfile; if you run inside Microsoft's Playwright container, bump its image tag to match. Until you can upgrade, pin the runner to an OS version your Playwright supports, such as runs-on: ubuntu-24.04 on GitHub. For Playwright and Vitest captures the upgrade does not shift your screenshots: UI Verify renders them in its own pinned browser, not the one your tests ran in.

Why did my build fail with a message about the project type?

You uploaded with a capture SDK, or a bundle format, that does not match how the project was created. A UI Verify project has one capture type, fixed when you create it: Storybook, Playwright, Vitest, or custom screenshots. Uploading a Playwright or Vitest archive to a custom-screenshots project, or the wrong SDK for the type, is rejected up front with a message naming the type the project expects, instead of failing later mid-render. The fix is to create a project of the matching type and upload to that one. See How to set up visual regression testing for the four types.

Why does the upload say there is nothing to upload, or the build fail with nothing to capture?

The directory you uploaded holds no captures. For Vitest or Playwright, the tests ran without the UI Verify capture SDK writing anything: check that Vitest runs in browser mode with uiverifyPlugin(), or that your Playwright tests import from @uiverify/playwright, then look inside ./uiverify-archive/snapshots before uploading. For Storybook, point --static-dir at your storybook build output. The failed build shows the reason on the build page and in the PR check. Fix the setup and upload again.

Why does the upload fail with an authentication error?

The UIVERIFY_API_KEY is missing or wrong. Locally, pass it inline (UIVERIFY_API_KEY=your_key npx -y uiverify@1.8.0 upload ...) or export it first; in CI, add it as a repository secret and reference it under env. The key is per project - copy it from that project's settings.

Why didn't my upload run on a pull request from a fork?

GitHub withholds repository secrets from workflows triggered by a forked-repo pull request, so UIVERIFY_API_KEY is empty and the upload step stops with UIVERIFY_API_KEY is required. This is expected, and it affects any secret-based CI - it does not happen for branches pushed to the same repo. If you take outside contributions, use the two-workflow setup in Visual testing on open source repos: the fork's run saves its capture, and a separate workflow on your default branch uploads it with your key.

Why does every story or test render on every build?

UI Verify skips the stories and tests your change cannot affect, but only when it can prove that from a dependency graph, so the upload log is the first place to look: when a Storybook or Vitest upload has no graph, the CLI prints a line starting Every story renders or Every test renders with the fix. The usual causes:

  • A Storybook built without --stats-json (npm run build-storybook -- --stats-json; --webpack-stats-json on Storybook 7), so there is no preview-stats.json to trace.
  • A Vitest archive from a @uiverify/vitest older than 1.6.0, or with uiverifyPlugin() missing from your root Vitest config.
  • A Playwright archive with no preview-stats.json added to it. UI Verify does not generate one for Playwright; see Module graph format.
  • --render-all on the upload, which turns skipping off.
  • A lockfile, package.json, build config or global stylesheet change, which can shift anything.
  • A first build with no confirmed baseline to carry forward yet.
  • A uiverify CLI older than 1.8.0. Upgrade it.

Each of these renders in full because that is the safe answer. See Skip unchanged stories and tests for how the decision is made.

Visual testing for agents

UI Verify is visual regression testing for Playwright, Storybook and Vitest. Your coding agent sees what its UI change did and fixes it before users see it. See how it works.

Get started