Skip to content

Troubleshooting

Find your symptom, then its fix. Each message RoutingCat prints is explained in the messages reference, and npx routingcat doctor, read check by check, names most of them.

init stops

init says Do this
Astro isn’t installed, or is older than 7 Install the site’s packages; upgrading Astro is your call: npx @astrojs/upgrade. Then run init again.
It can’t tell where the site deploys, with exit code 2 Run init again with --host vercel or --host cloudflare-workers.
The folder isn’t linked to Vercel, or the Vercel CLI is signed out Run npx vercel link or npx vercel login, then init again.
On Workers, the config has no Cloudflare adapter Run npx astro add cloudflare, then init again.
Wrangler is signed out, or reaches several Cloudflare accounts Run npx wrangler login, or set account_id in the Wrangler configuration, then init again.
Workers Cache is on for the Worker Remove its cache setting from the Wrangler configuration, then run init again.
It needs your production address, with exit code 2 On Workers, run init again with your address, as --origin https://www.example.com.
No project it recognizes Run init in your Astro site’s folder, beside its Astro config.
Installing the package failed Install routingcat yourself, then run init again.
The sign-in isn’t approved yet, with exit code 3 Approve the link, then run init again: it continues where it stopped.
The Vercel project belongs to a site you’re not a member of Ask that site’s owner to add you.
A file is too unusual to edit init prints the exact edit: make it, then run npx routingcat doctor, which checks it. In an Astro site, that is often a layout whose <head> opens with a component, several files holding <head>, or a custom entrypoint of your own on Workers.

Running init again is always safe: it does what’s missing and never overwrites your edits.

The build registers nothing

A build that registered prints RoutingCat’s receipt in its log, starting routingcat LIVE, NOT LIVE or registered. Without it, the deployment shows your defaults and records nothing, conversions included. The build log says why:

The build log says Do this
No Vercel token and no deploy key On Vercel, turn on Settings → Security → Secure backend access with OIDC federation, then redeploy. On Cloudflare, set ROUTINGCAT_DEPLOY_KEY in Cloudflare’s build variables, from npx routingcat keys deploy --branch <production branch>.
RoutingCat can’t be reached Redeploy once it’s back.
The build token or deploy key was refused Check ROUTINGCAT_DEPLOY_KEY, or that the Vercel project is still bound to your site. A production deploy key registers builds of its production branch alone. On your machine, for a site on Cloudflare, run the build with ROUTINGCAT_ENV=production instead: see Deploy from your machine.
The Vercel project is bound to another site Make site in routingcat.config.ts the project’s site.
No routingcat.config.ts Run npx routingcat init.
A local build Nothing, for a build that tries your pages on your machine. To deploy production to Cloudflare from your machine, run the build with ROUTINGCAT_ENV=production: see Deploy from your machine.

Once a production build registers, its deployment registers at its first request. If none has 10 minutes later, open your site; if it still doesn’t register, check RoutingCat’s entry: on Vercel, that middleware.ts exports middleware(…) from routingcat/vercel; on Workers, that the Worker’s main exports worker(…) from routingcat/cloudflare.

Pages show no decisions

A decision is RoutingCat’s choice of a visitor’s variants on a routed page, made by its middleware on Vercel or its Worker on Cloudflare. doctor checks each page’s first one.

  • RoutingCat didn’t run (the middleware or the Worker did not run). On Vercel, middleware.ts must export middleware(…) from routingcat/vercel, keep RoutingCat’s matcher entries and run on Node.js. On Workers, the Worker’s main must export worker(…) from routingcat/cloudflare, and run_worker_first keep RoutingCat’s patterns.
  • The page renders at request time under Astro’s edge middleware, on Vercel: see Pages rendered at request time aren’t routed.
  • The page isn’t live yet. Only a production deploy makes a page live, and on Vercel, for about 2 minutes after its first one, the page shows its defaults and records arrivals only.
  • Routing is off. The build log’s receipt says so; npx routingcat set routing=on turns it back on. When your plan turned it off, past Hobby’s visitors a second month running or at Pro’s cap, upgrade or raise the cap instead.
  • Routing waits for a card. On Hobby, it starts once the site’s owner has a card on file: see Routing waits for a card.
  • A slot has one variant. Nothing is routed there until you add a second.
  • A cache in front of your host serves the page (a cache serves it without RoutingCat). Stop caching routed pages’ HTML there: Run it in production.
  • The page comes over plain HTTP, on Cloudflare. RoutingCat leaves such views undecided, since a browser keeps none of its cookies from them: in Cloudflare, your domain → SSL/TLS → Edge Certificates → turn on “Always Use HTTPS”.
  • The middleware or the Worker lost access (nothing recorded for hours). Redeploy, which mints a fresh credential. On Cloudflare’s Free plan, a Worker also stops past its daily requests: RoutingCat needs Workers Paid.
  • Decisions come, but no render beacon. Keep <RoutingCat /> from routingcat/astro/components in your layout’s <head>. In your own browser, an ad blocker also stops it.

Pages rendered at request time aren’t routed

On Vercel, when the adapter deploys Astro’s middleware at the edge, with middlewareMode: 'edge' in vercel() or the older edgeMiddleware: true, that middleware serves your pages rendered at request time past RoutingCat’s. So RoutingCat routes none of those pages, and they show their defaults; the build passes, and your prerendered pages are routed as usual. The build log, doctor, status and the dashboard say so with the edge middleware warning.

Remove middlewareMode from vercel() in your Astro config, or set it to 'classic', Astro’s default, and remove edgeMiddleware: true if the config has it; then deploy.

A page shows its defaults to everyone

A page that carries every variant, prerendered or kept for every visitor, relies on <RoutingCat />’s head snippet to show each visitor’s variants. doctor names what stops it:

  • The page has no head snippet (the snippet is missing): render it through a layout whose <head> holds <RoutingCat />.
  • The page’s answer lost the visitor’s variants on the way (no rc entry): keep the Server-Timing header on your pages, which carries them, and which a header rule or a proxy in front can remove.
  • The page’s Content Security Policy blocks the snippet or its styles (the policy blocks RoutingCat): allow what the message names.
  • Cloudflare’s Rocket Loader delays the snippet (Rocket Loader runs it late): keep its tag as RoutingCat writes it, or turn Rocket Loader off for those paths, as Cloudflare’s settings shows.

Routing waits for a card

On Hobby, routing starts once the site’s owner has a card on file, and nothing is charged. Until then, every visitor sees your defaults, and:

The owner adds a card in the dashboard’s Settings → Billing, with nothing charged, or starts Pro’s 14-day trial. Routing starts within about 2 minutes, with no new deploy. A member asks the owner. See Add a card to start routing.

A variant never shows

  • Your browser keeps its variants for the whole visit, 7 days by default, and may be in the control, the 10% who always see the defaults. To see a variant, add ?rc=hero:learns in astro dev, or use a preview link in production: Change, preview and deploy.
  • It’s paused or retired. npx routingcat show home/hero:learns says so, and npx routingcat resume home/hero:learns brings it back.
  • The plan holds it. On Hobby, past the 10 live variants across the owner’s sites, a variant is registered and not served: see A variant is held.
  • It’s new, or routing favors others. A new variant starts on a limited share, and routing serves most what is likely best, though the random slice shows every variant now and then. show gives its share.
  • It was added during a visit. A slot added mid-visit shows its default to that visitor until their next visit.
  • Its build failed after registering (a build that failed after registering). On Vercel, the build registers before Astro prerenders your pages, so a build that fails while prerendering has registered already. Visitors keep the previous version: fix the build and redeploy, or revert the page.
  • It is a component. <RoutingCatSlot> renders variants made of fields, and a component variant renders nothing, with a warning in development: declare its content as fields.
  • ?rc= doesn’t show it in development. The development server reads your declarations when it starts: restart astro dev after adding a variant.
  • Browsers report it shown less often than the slot’s other variants (for a smaller share of the page loads it was picked for). Check that it renders as fast as they do.

A variant is held

On Hobby, the sites you own serve at most 10 live variants together, the slots’ defaults not counted. A deploy or an act past them still succeeds, and each variant past the 10th is held: registered, and not served. The build log names it after its first line, status reads held on its row, and the dashboard marks it “Held by the plan”.

To put it live, either:

  • upgrade to Pro in Settings → Billing, for every site you own, which allows up to 100 live variants a page;
  • or retire or pause a live variant on any of your sites, which frees room.

Held variants then go live in the order they were declared, within about 2 minutes, with no new deploy. See Go past Hobby’s 10 live variants.

Conversions don’t join

An Astro.locals.routingcat.track or .identify call that sends nothing says why on a line starting routingcat:: in your server’s logs for a track without an id or a build that registered nothing, and in the build’s log for a call from a prerendered page, which renders once, at build, for every visitor.

Track conversions explains the tracking line of status. The messages behind it:

The control’s share is off

status raises the control check when the control’s share of new visitors departs from its setting, which can bias the measured gain. Check that page for a cache, or a matcher that skips it for some visitors: middleware.ts’s on Vercel, run_worker_first on Workers.

What happens Do this
preview says the production deployment doesn’t carry the version Deploy it to production first.
preview says no deployment serving it has registered yet Right after a deploy, open your site, then run preview again. If the build failed after registering, deploy again.
preview says the page has no path to preview Pass one: --path /pricing.
preview refuses variants of several pages, or several of one slot Preview one page at a time, one variant per slot.
The page opens without the preview badge The link is valid for one hour: make a new one.
The badge names another variant The badge reads what the page shows. Right after a deploy, a new server instance can serve the page before it knows the variant: reload a few seconds later.

To end a preview, follow the badge’s link.

Run commands in a sandbox

Coding agents often run commands in a sandbox, as Work with your coding agent explains:

  • No network, exit code 4: allow network access for the command, or ask the person to run it.
  • The sandbox can’t keep a sign-in, exit code 3: run npx routingcat login in your own terminal, then the command again.
  • A sign-in link is waiting, exit code 3: give the link to the person, and once they approve, run the same command again.
  • A production build on the machine waits for the person, exit code 3: show them the plan it printed, with any warning beside it, and once they agree, run the build again with ROUTINGCAT_CONFIRM set to its digest, beside ROUTINGCAT_ENV=production.
  • doctor --wait runs up to 5 minutes: give it a 6-minute timeout, or run it in the background.

Read doctor check by check

npx routingcat doctor reads your project, your sign-in, your site’s records and one request to your production page, and changes nothing. That request is recorded as an arrival, flagged as RoutingCat’s own and kept out of results. After a deploy, --wait waits up to 5 minutes, printing each check as it passes, and stops early when only a person can move the rest. Each check prints one line, by the name below, with a message and its id when it fails.

Check Passes when Otherwise
Your files, shown only when one fails Astro 7.3.1 or later, and on Workers its Cloudflare adapter; init’s edits; Workers Cache off; the site’s public key; an up-to-date skill Astro, Workers’ needs, an edit to make, Workers Cache, the wrong public key, the skill
build registered A production build registered, with the objective and outcomes it declares Waits for a production deploy
deployment registered The deployment serving that build registered at its first request No deployment registered after 10 minutes
runtime, on Vercel The deployment reports the Node.js version middleware.ts runs on Another runtime
Worker version, on Workers The deployment registered under its Worker’s version No version metadata binding
middleware on /, or Worker on / Your production page set rc_vid RoutingCat did not run, or a cache in front answered, as Cloudflare’s Cache Everything does. If a firewall challenges the check’s request, doctor reads the pulse instead.
/_rc Your site’s /_rc path reaches RoutingCat /_rc is missing
head snippet Your pages carry <RoutingCat />’s snippet in their <head> No head snippet
selection, once a page is routed Each routed page that carries every variant answers with its visitor’s decision No rc entry, a cache in front answered, as Cloudflare’s Cache Everything does, a shared cache answered a page rendered for one visitor, or a build made for development on a person’s machine
policy Your pages’ Content Security Policy lets RoutingCat run, apply and record What the policy blocks
Cloudflare’s features, shown only when one fails Rocket Loader running the head snippet late; on Workers, pages served over plain HTTP
records RoutingCat accepts the records of your production address The address isn’t one of your domains
first arrival The pulse shows an arrival, often the check’s own request Waits about a minute
The objective, by its name A first conversion arrived No event yet: waits for the first real conversion, a visitor’s or your own, and nothing else waits on it
card on file, on Hobby The site’s owner has a card on file, shown by its brand and last 4 digits Waits for the owner to add one, at the link it gives: routing waits for a card
page live, first decision, first render beacon Each routed page is live, decided, and shown in a browser Waits for a visit; no render beacon, or a cache in front
Astro middleware, server key, location headers, shown only when one fails Astro’s middleware at the edge, the preview key used in production, Cloudflare without its location headers

Lines marked · are information and need nothing: conversions with no routed visit yet, an undeclared name, and, run in your project, whether AGENTS.md has the note for coding agents, which is your choice.

doctor exits with 0 when every check passed, 1 when one failed, 3 when one is still waiting, and 4 when the network blocks it.

Next