Troubleshooting
An audit says a package is not installed
! a11y: "pa11y" is not installed. Add it as a dev dependency: npm i -D pa11y
The browser audits use tools you install next to vidimus; they are optional peer dependencies,
so npm does not install them for you. A missing peer errors only the audit that needs it; the
others still run, and the run exits 1.
| Audit | Install |
|---|---|
a11y | puppeteer, pa11y |
links | linkinator |
r12s, privacy | puppeteer |
html | html-validate |
shots | puppeteer, and sharp for motion GIFs |
lighthouse | lighthouse, puppeteer |
If you do not want an audit, take it out of audits or set its severity to off instead of
installing its peer. npx vidimus csp seo budget runs without any peer installed.
Chrome does not start
With the peers installed, an audit can still error when Chrome cannot launch; its summary is puppeteer’s error message.
- Could not find Chrome (or
chrome-headless-shell, whichshotsuses): puppeteer’s browser download was skipped (PUPPETEER_SKIP_DOWNLOAD,--ignore-scripts, a package manager that blocks install scripts) or the cache path differs. Runnpx puppeteer browsers install chrome(andchrome-headless-shellforshots), or setbrowser.executablePathto a Chrome or Chromium already on the machine. - Missing shared libraries (
error while loading shared libraries: libnss3.soand similar): the system lacks Chrome’s dependencies, common in slim container images. Install a distribution Chromium, which pulls them in, and pointbrowser.executablePathat it; see CI. - No usable sandbox: Chrome refuses to run as root with its sandbox. vidimus passes
--no-sandbox --disable-dev-shm-usageby default throughbrowser.args. If you setbrowser.args, the list replaces the defaults: include both flags again.
The build is not found
vidimus: no build output at /path/to/site/dist. Run the build first.
Every audit except i18n needs the build. Run it first, and set distDir to where your
generator writes; Frameworks lists the usual directories. distDir resolves
from root, which is the config file’s directory, not necessarily the working directory: with
-c docs/vidimus.config.json, distDir: 'dist' means docs/dist. npx vidimus config shows
the resolved root and distDir.
<dist> has no HTML pages. Rebuild. means the directory exists but, after the top-level
exclude, holds no .html file.
Pages or assets 404 under a base path
A site built for https://user.github.io/project/ links to /project/…. If siteUrl does not
include /project, the built-in server does not know about the prefix and every link and asset
looks broken to links, a11y and the other browser audits.
Set siteUrl: 'https://user.github.io/project'. The build is then served under /project, and
paths without the prefix still resolve. With --origin, include the path too:
--origin http://localhost:4173/project. Details in
How it works.
server.headers rules match the request path with the base path, so ^/_astro/ does not
match /project/_astro/…. Drop the ^ or include the prefix.
Canonical, sitemap or Open Graph findings mention another origin
✖ canonical points outside https://example.org
✖ sitemap URL outside https://example.org
✖ og:url origin https://www.example.org differs from siteUrl https://example.org
These checks compare the origin (scheme, host and port) with siteUrl. The usual causes:
www.in one and not the other, orhttpagainsthttps- a generator setting (
site,baseURL,url) that differs fromsiteUrl, or a preview build made with a staging URL siteUrlleft empty: the origin checks are skipped, and absolute links to your own site are checked against the live site instead of the build
Set the generator and siteUrl from the same value. A config function can read it from the
environment: siteUrl: env.SITE_URL ?? 'https://example.org'.
Only one page is audited
A client-rendered app builds one index.html, and the page list is the HTML files in
distDir, so every server audit checks the home route only. The pretty reporter says so at
the start:
vidimus: dist has one HTML page and a 412 kB script: looks like a client-rendered app; set routes.paths or routes.discover to audit its routes
Set routes to list the other routes, read them from a sitemap, or crawl them, and
server.fallback so the built-in server answers them with index.html. See
Frameworks.
seo says there is no title, but the app sets one
seo, html, csp and assets read the built HTML file, and in a client-rendered app that is
the shell before any script runs: no <title>, no description, no <h1>. Set
render.mode: 'on' (or 'auto') so they read the DOM a browser renders, with
render.waitFor set to something that appears once the app has rendered. See
How it works.
Search engines that do not run JavaScript see the shell too; prerendering fixes both.
shots differ on every run
Something on the page changes on its own. Open .vidimus/shots/diff.html and look at where the
red is:
- a canvas or WebGL scene: keep
shots.freezeon, or make the scene render one frame underprefers-reduced-motion: reduce, which the main screenshot uses; - an embedded map, video player or widget:
shots.maskEmbedsmasks cross-origin iframes; add anything else toshots.mask; - content a script fetches after load: set
render.waitForto a selector that appears once it is in, or to'networkidle'; - the text of the page itself, across machines: fonts render differently on each OS, so record the baseline on the same kind of machine that compares it.
See shots.
Timeouts and slow runs
The non-exclusive audits run at the same time, and each browser audit opens several tabs. On a small CI runner that can mean many pages loading at once.
| Audit | Page timeout | Parallel tabs |
|---|---|---|
a11y | a11y.timeout, 60000 | a11y.concurrency, half the cores |
r12s | r12s.timeout, 60000 | r12s.concurrency, half the cores |
privacy | privacy.timeout, 60000 | privacy.concurrency, half the cores |
links | links.timeout, 20000 per request | links.concurrency, 25 |
shots | shots.settleTimeout, 10000; shots.protocolTimeout, 600000 | shots.concurrency, half the cores |
Half the cores means between 2 and 8. When pages time out:
- lower the concurrency of the browser audits, or run them in separate steps
(
npx vidimus a11y, thennpx vidimus r12s) - raise the audit’s timeout
- use
sample(a11y,r12s,privacy,shots,lighthouse) to open one page per template - leave heavy sections out with the audit’s
exclude
In a11y, privacy and r12s a page that fails to load becomes a finding (failed to audit …, failed to load …) and the other pages are still checked.
External links fail intermittently
A broken link finding is <status> <url>, or ERR <url> when there was no response. The fix
line depends on the status:
404/410: the target is gone. Fix or remove the link.- other
4xx(often403or429): the target blocks bots. If it works in a browser, add a pattern tolinks.skip. 5xxorERR: the target was down or slow. Raiselinks.timeout, keeplinks.retryon, or skip it.
For pull request builds that should not depend on other sites, set links.checkExternal: false
and check external links in a scheduled job. links.skip patterns are regular expressions
matched against the link URL: '^https://(www\\.)?linkedin\\.com/'.
A config typo fails the run
vidimus: /path/to/site/vidimus.config.ts: unknown config key "seo.titleLenght"
Unknown keys in a config file or --set stop the run with exit code 2; that is intended.
npx vidimus config --no-config lists every valid key. ignore rules are checked too: an
unknown key, an empty rule {}, a value that is not a string or an invalid regular expression
also exits 2. Two places are not checked:
VIDIMUS_*variables that do not name a key are ignored- entries of other arrays, such as
server.headersrules: a misspelled key there is not reported
Values in a config file are not type-checked when loaded. Use the $schema in JSON configs or
defineConfig in TS configs to catch those in the editor.
Findings disappear or will not go away
- The summary line counts hidden findings:
3 ignored or accepted. They come fromignorerules or the baseline file. --set 'ignore=[]'runs without ignore rules,--set baseline.file=without the baseline, and--set 'severity={}'with every audit back aterror.- A baseline entry matches audit, message, file and details. A finding whose message contains a number (a size, a score) comes back when the number changes. See Adopting on an existing site.
Lighthouse says robots.txt is not valid
Lighthouse fetches robots.txt from inside the page, so a Content-Security-Policy without
connect-src (for example default-src 'none') blocks the request and the SEO category loses
the robots-txt audit, even when the file is fine. The seo audit reads the
file from the build and is not affected. Add connect-src 'self' to the policy, or accept the
lower score with lighthouse.thresholds.seo.
The port is in use
The built-in server listens on 127.0.0.1:4322. When another process holds the port, the run
stops with Node’s EADDRINUSE error and exit code 2. Pick another with --port 5000 or
VIDIMUS_PORT=5000. With server.command, vidimus checks the port before starting the command
and stops with port 4322 is in use when something already answers there.
server.command did not start
vidimus: server.command exited with code 1: npx wrangler pages dev /path/to/dist --port 4322
✘ [ERROR] …
(full output in /path/to/.vidimus/server.log)
The command exited, or did not answer on port within server.startTimeout. The last lines of
its output are in the message, all of it in server.log in outDir. Common causes: the tool is
not installed, it asks a question (pass its non-interactive flag, such as --yes), it needs a
login in CI, or it listens on a port of its own config file instead of {port}; set port to
that number. A slow first start (downloading a Docker image) needs a larger
server.startTimeout.
Exit codes
The full table is in CLI. An audit that errors, from a missing peer or a
browser that did not start, exits 1 like a failure; a problem before the audits start, such as
config, a missing build or a port in use, exits 2. Warnings exit 0 unless --strict is set.
A skipped audit (○) is not a failure: csp without a meta CSP, or i18n without
i18n.files, locales and defaultLocale.
These docs are deliberately built 8 times, with VitePress, Starlight, Hugo, Eleventy, Zola, mdBook, React and Angular, from the same markdown, and audited by vidimus as one site. Why.