Troubleshooting
The most common ai-visibility issues, and four framework-specific gotchas verified by actually building and running each integration — not assumed from the framework's own docs. Ported and corrected from the package's repo docs/troubleshooting.md against its current 0.4.0 behavior, since a couple of entries there predate the 0.3.0 subpath exports and are now stale.
Framework gotchas
These four cost real debugging time while the package's framework recipes were built and verified — see the recipes page for the full working code each of these applies to.
Nuxt: a scaffolded robots.txt silently wins
nuxi init scaffolds a placeholder public/robots.txt. Nitro serves static files from public/ before dynamic server routes at the same path, so that placeholder silently shadows a server/routes/robots.txt.ts generated with RobotsGenerator— the route never runs. This is easy to miss because the output can look identical between "shadowed" and "working" at a glance: the scaffold's placeholder happens to also read User-Agent: * / Disallow:. Delete public/robots.txt (and check for public/llms.txt too) before assuming a dynamic route isn't working.
@unhead/vue v3: createHead moved to /client
Current @unhead/vue(v3), confirmed by reading the package's own type declarations rather than assuming the old API still works: createHead moved to the /client subpath — import { createHead } from "@unhead/vue/client". The root-level createHead export still exists but is marked @deprecated, will be removed in v4. useHead() itself is unchanged and importable from the root as before.
React Router 8: NODE_ENV must match on build and serve
Serving a production React Router build without NODE_ENV=production set explicitly for both the build and serve steps throws TypeError: dispatcher.getOwner is not a function — a React dev/prod build mismatch inside the server bundle, unrelated to ai-visibility itself but hit while verifying the React Router recipe. Fix:
"build": "NODE_ENV=production react-router build",
"start": "NODE_ENV=production react-router-serve ./build/server/index.js"detectAndOptimize() import crash on 0.3.0 and earlier
Through 0.3.0, detectAndOptimize() was documented as framework-agnostic but only importable from ai-visibility/next, whose module scope statically imports next/server. Importing it in a Nuxt, Vue, or plain Node project crashed with Cannot find module 'next/server', even though the function itself never touches Next.js. Fixed in 0.3.1 by moving it to its real home, ai-visibility/detector (still re-exported from /next for existing imports):
// Before 0.3.1 — crashes outside a Next.js project
import { detectAndOptimize } from "ai-visibility/next";
// 0.3.1+ — works anywhere, no framework required
import { detectAndOptimize } from "ai-visibility/detector";See the migration guide for the full version-by-version breaking-change history.
Middleware and detection
req.isAIBot is undefined (Express)
createAIMiddleware() must run before any other middleware that reads req.isAIBot/req.aiBotInfo:
app.use(createAIMiddleware()); // must be first
app.use(optimizeResponseForAI()); // then optimization
app.use(express.json()); // then everything else
app.use(yourRoutes);A specific crawler isn't being detected
Log the incoming User-Agent and check it against detectBot() directly, add it via additionalBotsif it's missing from the registry, or enable verbose mode to log every detection:
app.use(createAIMiddleware({
additionalBots: ["MyCustomBot"],
verbose: true,
}));If it's a real AI crawler that should already be in the registry, check the crawler registry reference— it may be an unverified entry, a recent token change on the vendor's side, or genuinely missing and worth filing as an issue.
optimizeResponseForAI() is stripping content you need
Disable the specific option responsible rather than the whole call:
app.use(optimizeResponseForAI({
stripJs: false, // keep <script> tags
removeAds: true,
removeTracking: true,
simplifyNav: false, // keep navigation intact
}));Schema and content generation
Importing SchemaBuilder
Correction to the package repo's own troubleshooting doc, which currently says subpath imports for SchemaBuilder don't work — that predates the 0.3.0 subpath exports and is now wrong. Verified directly against the installed 0.4.0 type declarations: both the root barrel and the dedicated subpath work, and the subpath is the one worth using if you only need schema building (it's dependency-free except the lazy fromHTML()):
// Both of these work as of 0.3.0+:
import { SchemaBuilder } from "ai-visibility"; // root barrel
import { SchemaBuilder } from "ai-visibility/schema"; // dependency-free subpath
import type { ProductSchemaData, FAQItem } from "ai-visibility/schema"; // types too, since 0.4.0llms.txt generation times out
autoSummarize: true fetches each page live (5-second timeout per URL) to extract a summary. For large sites, slow servers, or offline builds, provide summaries manually instead:
const gen = new LLMSTextGenerator({
siteName: "MyApp",
description: "My App",
pages: [{ url: "/docs", title: "Docs", summary: "Complete documentation" }],
autoSummarize: false,
});Schema auto-detection picks the wrong type
SchemaBuilder.fromHTML() uses heuristics and can guess FAQPage on generic content. Pass hints, or build the schema manually instead of relying on detection:
const schema = await SchemaBuilder.fromHTML(html, {
author: "Jane Doe",
publisher: "My Blog",
}); // now more likely to detect ArticleContent analyzer
A low ContentAnalyzer score on content you think is strong usually traces to one breakdown metric, not the whole analysis:
| Low score | Likely cause | Fix |
|---|---|---|
| answerFrontLoading | Main answer not in the first paragraph | Move the key fact to the top, before supporting detail |
| factDensity | Too few numbers/dates/percentages | Add concrete facts — figures, dates, named sources |
| headingStructure | Multiple H1s or skipped levels | One H1, then H2 → H3 in order, no jumps |
| eeatSignals | No author/organization/contact info | Add <meta name="author">, organization info, contact details |
| snippability | Sections don't stand alone | Give each H2 a substantial paragraph directly under it |
| schemaCoverage | Missing JSON-LD | Add a SchemaBuilder schema for the page's content type |
For a live, opinionated version of these same checks against a real deployed page (not just a raw HTML string), run the free CrawlPod scan instead.
Logging
AIVisitorLogger's memorystorage doesn't survive a restart; filestorage doesn't survive on serverless platforms where the filesystem resets between invocations (Vercel, most edge/serverless hosts). Use storage: "both" for local dev, or write to your own persistent store (a database) in production — see /built-withfor how this site logs crawler visits to Postgres instead of using this class's storage at all.
Getting help
Check the API reference for exact signatures, or open an issue on GitHub with your Node.js version, the installed package version, and a minimal reproduction.