Troubleshooting
Indexed by symptom, because you know the symptom and not the cause. Several of these fail in ways that look like success.
astro build fails on [...path].ts
Section titled “astro build fails on [...path].ts”Missing export const prerender = false. A prerendered dynamic route with no
getStaticPaths() fails the build outright, and Astro prerenders by default. The Geoffy
namespace.
This one is at least loud. The next entry is the same mistake, quiet.
The namespace works in astro dev and not in production
Section titled “The namespace works in astro dev and not in production”astro dev renders every route on demand, so a missing prerender = false is invisible
there. Run astro build && astro preview: preview serves the real output. Testing
locally.
Nothing renders on the product page
Section titled “Nothing renders on the product page”| Cause | Fix |
|---|---|
GEOFFY_SITE_KEY is not set where the page runs | Nothing is requested, and the server log shows @geoffy/headless: GEOFFY_SITE_KEY is not set once. Set it for that environment and restart |
| A Shopify store’s own site is not verified yet | Every artifact endpoint 404s until it passes. A site with no commerce platform is served before the check. Prove you own your domain |
| Nothing is published for that handle | The endpoints serve published bundles only. Publish in the dashboard first |
Wrong handle | With no commerce platform it is the segment your URL pattern captured, not your slug. The product page |
canonicalUrl does not match | The helper returns null and your guards render nothing. There is no marker on this side, so check the handle first |
| Another page has the same handle, such as the same slug in two languages | Before 0.3.5 only the page published last renders, and the helper returns null on the other. Upgrade to 0.3.5 and pass this page’s own canonicalUrl. Several languages on one site |
| The page was prerendered before you published | Static builds bake the markup in. How updates arrive |
The structured data is in the body, not the head
Section titled “The structured data is in the body, not the head”Your Layout.astro does not declare <slot name="head" /> inside its <head>, so the
fragment falls through into the body. It still verifies and is still read, so fix it when
convenient. Where to put the markup.
The widget renders, but there is no structured data
Section titled “The widget renders, but there is no structured data”Correct when your site owns its structured data: the Structured data setting is Never, or the page already carries its own node of the same type. Geoffy then serves the widget alone. If you expected structured data, check that setting in Geoffy, and check the page for a node of the same type that your site already emits.
The widget is gone after you set Structured data to Never
Section titled “The widget is gone after you set Structured data to Never”Your @geoffy/headless is older than 0.3.4, which renders nothing when Geoffy sends no
structured data. Upgrade the package.
The widget renders unstyled
Section titled “The widget renders unstyled”Something is stripping the <style> element that travels inside widgetHtml: reaching
for a sanitiser instead of set:html, most often.
My robots.txt never changed
Section titled “My robots.txt never changed”A static public/robots.txt wins over src/pages/robots.txt.ts. Delete it. This is the
single most common cause of “I added the route and nothing happened”. Crawler
rules.
My robots.txt has no Sitemap: line for Geoffy
Section titled “My robots.txt has no Sitemap: line for Geoffy”On a static build the file is generated once, at build time, and the line is withheld until the namespace is mounted. So you built before mounting. Mount first, then deploy.
createGeoffyTextEndpoint refuses robots-rules.txt
Section titled “createGeoffyTextEndpoint refuses robots-rules.txt”Deliberately. It serves whole files; the robots rules are a fragment you append to yours. Crawler rules.
A publish does not show up
Section titled “A publish does not show up”If you are fully static, that is the answer: content appears at your next build, and there is no deploy hook or acceleration. If you are server-rendered, it is the cache window: one hour by default. How updates arrive.
The mount is reported as not done, but the URL works in my browser
Section titled “The mount is reported as not done, but the URL works in my browser”A 200 is not the check. Three things must be true, and a catch-all answering with your home page satisfies none of them. Checking it worked.
wrong_site means our marker came back carrying a different site key: your
GEOFFY_SITE_KEY belongs to another Geoffy site.
Products index slowly, and nothing is wrong
Section titled “Products index slowly, and nothing is wrong”Probably the IndexNow key. The namespace authorises only what is under /apps/geoffy/, so
product-page pings are discarded unless you also serve public/{key}.txt. Nothing errors
when you skip it. The IndexNow key.
GEOFFY_ORIGIN is ignored
Section titled “GEOFFY_ORIGIN is ignored”It is read from process.env, which an edge runtime does not have. Pass origin alongside
siteKey instead. A value that is not an absolute http/https URL is also ignored, with
no error, so a typo looks the same as this.
No AI visits show up in Geoffy
Section titled “No AI visits show up in Geoffy”| Cause | Fix |
|---|---|
| No secret | Pass secret: import.meta.env.GEOFFY_REVALIDATE_SECRET. Without a secret nothing is sent |
| The pages are prerendered | A prerendered page is served as a file and never reaches middleware. Add export const prerender = false to the pages you want counted |
Another onRequest replaced it | Combine the two with sequence() from astro:middleware |
| Nothing qualified | Only AI crawlers and visitors from an AI assistant are reported. Your own browser visits are not, by design |
| The package is older than 0.3.1 | Upgrade. 0.3.0 sends one visit per call, so a crawler sweep past 60 pages a minute is mostly dropped |
AI visit tracking has the full setup.
AI visits show localhost, or crawler visits are never confirmed
Section titled “AI visits show localhost, or crawler visits are never confirmed”Both are a self-hosted site behind a reverse proxy.
| Cause | Fix |
|---|---|
Visits are reported on localhost or an internal address | Upgrade to 0.3.2 or later, and have your proxy forward Host, X-Forwarded-Host and X-Forwarded-Proto |
| Crawler visits arrive but none is confirmed | No trusted visitor address reaches the middleware. Have your proxy overwrite X-Real-IP and pass clientIp: (request) => request.headers.get("x-real-ip") |
Self-hosted (not Vercel) has the proxy settings.
A plain-text twin has no Link header
Section titled “A plain-text twin has no Link header”Each .md twin under /apps/geoffy/ names its HTML page in a Link: <https://…>; rel="canonical"
header. If the check
finds no such line:
| Cause | Fix |
|---|---|
@geoffy/headless is older than 0.3.3 | Upgrade. Earlier versions do not pass the canonical Link on |
| A CDN or a middleware in front of the endpoint removes it | Let the Link and X-Robots-Tag headers through on /apps/geoffy/* |
Still stuck
Section titled “Still stuck”Check what your own site is actually serving:
curl -si https://yourdomain.com/apps/geoffy/sitemap.xml | head -20curl -s https://yourdomain.com/your/product/page | grep -c 'data-geoffy'curl -s https://yourdomain.com/llms.txt | head -20Then compare against the source. Your site’s settings in Geoffy show the direct URL for each of your published surfaces; fetching one bypasses every cache of yours at once. If Geoffy and your site disagree, the problem is on your side of the fetch.