Skip to content

Troubleshooting

When cora stops with an error, or a cora-mcp tool returns one, start with the error’s entry in the error reference. It says what the error means and what to do about it. The docs: line under the error, and a cora-mcp error’s docsUrl, link that entry.

Symptom Go to
cora scan exits with code 2, and says Gate incomplete A target was not scanned
A page’s findings change from one run to the next Findings that differ between runs
Pages behind a sign-in pass as the sign-in page Pages behind a sign-in were scanned as the sign-in page
A server.* error stops the scan server.already-running, server.exited or server.timed-out
A browser.* or playwright.* error browser.not-installed, browser.launch-failed, playwright.not-installed or playwright.unsupported-version
A config.* error config.invalid, config.ambiguous, config.load-failed or config.invalid-environment
A CI job fails with account.missing No token reached the CI job
The CI artifact holds no report The uploaded report is empty
cora report merge refuses the shards merge.incomplete-shards, merge.duplicate-shard or merge.no-inputs
An account.* or service.* error account.expired, account.invalid, service.client-outdated or service.unavailable, and for service.unreachable, cora cannot reach the CORA service
A certificate error, such as SELF_SIGNED_CERT_IN_CHAIN Behind a TLS-inspecting proxy
A coding agent cannot start or use cora-mcp Coding agents
A cora-mcp scan or browser tool fails with mcp.state-unwritable cora-mcp cannot write its state
cora mcp or a cora-mcp tool fails with an error mcp.release-unavailable, mcp.invalid-argument, mcp.browser-timeout, page.unresponsive or account.missing
You need more detail, or a log file Logs and bug reports

When no new violation fails the gate, cora scan exits with code 2, and its summary says so. It lists each target it did not scan, with why in words and the detail after it in parentheses:

Not scanned:
http://localhost:4173/: the page did not load (page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:4173/)
Gate incomplete: 1 target was not scanned, so the run could not confirm a pass
Targets: 1 (1 not scanned)
Issues: 0
Duration: 2.4 s
Results: .cora/runs/20261006T171204523Z-p7m2q9xd.json

The run still wrote its results file, which gives each target that was not scanned a coverage reason, a code with the same detail. In CI, the report directory’s summary.md lists them too. The common reasons, by the words the summary gives and their codes:

  • the page did not load (navigation-failed): the app was not running, or the page answered with an HTTP error.
  • the page redirected to another page (redirected): the page sent cora to a page the run did not scan, such as a sign-in service on another host.
  • the scan ran out of time (timed-out): the visit outlasted page.timeout. Raise it, or retry the visit with page.retries.
  • the page never became ready (not-ready): the config’s page.waitFor threw, or is not valid CSS.
  • the page stopped answering (page-unresponsive): usually an open alert() blocked the page.

The coverage reasons reference lists every reason. Redirects and failures says how cora scan records them. Exit code 2 also means an error stopped the command: the exit codes say how a CI script tells the two apart.

The summary says a target was scanned before it settled

Section titled “The summary says a target was scanned before it settled”

cora scan scanned the page while it was still changing, so its findings may differ between runs. The summary counts it as scanned before it settled. Findings that differ between runs says what it waited on, and what to change.

Pages behind a sign-in were scanned as the sign-in page

Section titled “Pages behind a sign-in were scanned as the sign-in page”

Each page redirected to the sign-in page. The results file records each as redirected, with the page it landed on. When the sign-in page is on the same host, cora scans it as a target of its own, and the gate counts each redirect as scanned there.

  • Give cora a signed-in browser, with browser.storageState or browser.setup: see Signing in.
  • Make browser.setup fail when sign-in fails, such as by waiting for the signed-in page. A setup that throws fails the scan with signin.setup-failed.
  • A storage-state file holds session cookies, which expire. Save the file again when its session ends. A file that is missing fails with signin.storage-state-not-found.

The job fails with account.missing and exit code 2. cora found no CORA_TOKEN, and a CI runner holds no signed-in credential.

  • Set a CI token as CORA_TOKEN in the CI system’s secrets, and pass it to every step that runs cora scan, cora report merge or cora baseline update.
  • In Azure Pipelines, a secret variable reaches a script only when the step maps it in its env.
  • A pull request from a fork gets no secrets, and neither does a Dependabot run unless the secret is also one of Dependabot’s. Skip the scan there with a notice, as Your account in Running in CI shows.

.cora/ is hidden, since its name starts with .. actions/upload-artifact skips hidden files unless its include-hidden-files is true.

cora scan writes the report directory only in CI, or where --report names one. Outside a project, it lands in .cora/report/ in the folder the job runs cora in. The report directory lists what it holds.

A command fails with service.unreachable. The machine may be offline, a firewall may block the connection, or a proxy may refuse it. The message says what failed, with the network error’s code, such as ECONNREFUSED, or gives the timeout it reached.

  • On a network that reaches the internet only through a proxy, name it in HTTPS_PROXY: see Proxies.
  • On a slow proxy or VPN, raise the request timeout.

A proxy such as Cloudflare WARP, Zscaler or Netskope signs each HTTPS connection again with a root certificate of its own. Without that root, a request fails with service.unreachable, and its message gives the TLS error’s code, such as SELF_SIGNED_CERT_IN_CHAIN.

Ask your IT team to install the root in the operating system’s store, which cora and cora-mcp trust. Or save it as a PEM file, and name the file in NODE_EXTRA_CA_CERTS. Trusted certificates covers both, and the Node.js releases that read less of the store.

cora-mcp logs on stderr, which the agent’s client keeps. Its tools return their errors to the agent, each with a code from the error reference. Seeing cora-mcp’s stderr shows where each client keeps it.

Where cora-mcp finds no display, its browser runs without a window, and cora-mcp warns setting ignored as it starts, naming mcp.headless and no-display. Under Codex on Linux, a desktop’s display reaches an entry of your own only when its env_vars names DISPLAY.

Where a display is set but refuses the window, the browser fails to start with browser.launch-failed. Run it headless with --headless in the entry, CORA_MCP_HEADLESS=1, or mcp.headless in your user config: see Headed or headless.

The client stopped waiting while npm downloaded cora-mcp.

  • Codex waits 10 seconds by default: raise the entry’s startup_timeout_sec.
  • Claude Code waits 30 seconds: raise MCP_TIMEOUT, in milliseconds, as in MCP_TIMEOUT=60000 claude.
  • Or fetch the release before the agent starts. npx cora init fetches it as it sets a coding agent up, and npx --yes --prefer-offline=false @vispero/cora-mcp@<version> --version puts it in npm’s cache.

When it fails on every start, not only the first, see the server fails to start when npm’s proxy or registry can’t be reached.

The server fails to start when npm’s proxy or registry can’t be reached

Section titled “The server fails to start when npm’s proxy or registry can’t be reached”

npx is waiting for a connection that never opens, to npm’s registry or to the proxy that npm’s settings or HTTPS_PROXY name, such as a corporate proxy while you’re off its VPN. It waits rather than starting cora-mcp from npm’s cache, for longer than your agent’s startup timeout. Raising that timeout helps only where the network drops the connection, and then every start waits for the operating system to give up. On that network, run npm config set offline true, or remove the proxy from npm’s settings and HTTPS_PROXY and https_proxy from the agent’s environment, then restart the server:

  • In Claude Code, open /mcp, select plugin:cora:cora and choose Reconnect. A new session skips a server that failed to connect in the last 15 minutes.
  • In Codex, start a new session.

Run npm config delete offline once you’re on a network that reaches the registry again, since offline applies to every npm command on the machine and fails a release npm hasn’t cached (npm’s registry).

cora-mcp warns matching release unavailable

Section titled “cora-mcp warns matching release unavailable”

cora-mcp could not fetch the project’s release in time, so it serves with the release the agent started. That release’s rules and fingerprints may differ from the project’s. Run the command the warning gives, then restart the server. Version alignment says how cora-mcp picks its release.

npm fails with ENOENT on a node/lib path when cora init or cora mcp runs it through pnpm, as cora --debug mcp shows. When pnpm manages the project’s Node.js, through devEngines.runtime, pnpm exec and pnpm cora put pnpm’s own Node.js first on the PATH, and npm cannot find its files there. Fetch the release from a shell where node is your own installation: npx --yes --prefer-offline=false @vispero/cora-mcp@<version> --version.

A scan, verify_fix, summarize_fixes or a browser tool fails with mcp.state-unwritable, naming the state directory it could not create or write. cora-mcp keeps its state in .cora/ in the folder the agent’s client started it in, the folder you opened in the agent, and that folder is one it cannot write, such as a system folder. The tools that only read still work there.

  • Open a project folder, or another folder you can write, in the agent.
  • In Codex, set the cwd of cora-mcp’s entry to such a folder.
  • Or set CORA_STATE_DIRECTORY in the entry’s environment to a directory cora-mcp can write (The state directory).

Claude Code’s agent does not use CORA’s tools

Section titled “Claude Code’s agent does not use CORA’s tools”

Check that /mcp shows the server connected. Claude Code loads the tools of a server you add on demand, through its tool search: Claude Code says how to load them as every session starts.

The project agent, cora-a11y-fixer, says CORA’s MCP server is not connected.

  • Claude Code starts the agent’s own server only in a folder you trusted.
  • On its first start, npx may still be downloading cora-mcp when Claude Code stops waiting. Dispatch the agent again, or let npx cora init fetch the release ahead of time as it sets Claude Code up.
  • When the agent says CORA’s plugin is not enabled, install the plugin, as In Claude Code shows: the agent’s skills come from it. cora init enables it in the project’s settings, but each person installs it once.

The project agent describes its setup.

Check each of what the hooks need. Under Codex, they also wait until you trust them, and Claude Code’s disableAllHooks turns them off.

A finding that comes and goes between runs of the same page usually comes from a page still changing while it is scanned. Each run then reads it at a different moment.

cora scans a page once it settles, as Waiting for a page describes. When it gives up first, it scans the page as it is:

  • The results file records the target as unsettled, with the settle timeout that passed.
  • cora scan’s summary counts it as scanned before it settled. The report directory, the GitHub Actions annotations and cora-mcp’s scan results name it too.
  • The page did not settle warning names in waitingOn what settling still waited on.

What each waitingOn value means:

  • dom: the page’s documents kept changing, such as a ticker or a carousel that never stops.
  • load: the page was still loading, such as a stylesheet or an image that had not arrived.
  • frames: an iframe was still loading.
  • fonts: a font was still loading.
  • animations: an animation that ends, such as a slow fade, was still running, or had ended within the quiet window.
  • requests: a request for a document, a stylesheet, a script, a font, an image or data was still in flight, or had ended within the quiet window.

Then, for the page’s route in routes:

Findings that differ between machines, rather than between runs, can come from each machine’s locale or time zone. Every page loads in the config’s browser.locale and browser.timezone, whatever the machine’s own: see Locale and time zone.

Send a bug report to cora@vispero.com, with:

  • what you ran, and what happened;
  • the output of npx cora info, or the JSON npx cora info --json prints;
  • the crash file, when cora or cora-mcp wrote one.

Logs and bug reports says what cora info prints, and What logs contain what to review in a file before you send it.