agentblog.dev/docs

Troubleshooting

The failures that actually happen, what each one looks like from the outside, and the fix for each.

Almost every failure here is silent. That is the shape of the problem rather than an accident of implementation: metadata in the wrong element still renders, a narrowed pattern still matches, a stale cache still returns 200. So the symptom is usually "nothing is wrong and nothing is working".

Start here. If the answer turns out to be "a CDN is blocking the crawler", that has its own page, because it is the most common cause and the least visible one.

The build fails after I edited content

This is the first thing most installs hit, and doctor sends you into it: it warns that the example author roster is unedited, you edit it, doctor goes green, and the next build fails. doctor reads your config, not your posts, so the example posts are outside what it checks.

[agentblog] content/blog/how-ai-search-engines-read-your-blog.mdx is invalid:
  author: unknown author slug "editorial". Add a record with that slug to
  content/authors.json, or inline the author object in this post's frontmatter.

Both example posts carry author: editorial, and editorial is the slug of the first record in content/authors.json. Replacing the roster with your own record under a different slug deletes a slug two files still name. The category version of this error is identical in shape.

Three ways out, in the order they are usually right:

  1. Keep the editorial slug and replace everything else in the record. The slug is an internal key. It appears in one URL, /authors/editorial, and nowhere else. Edit the name, bio, job title, and profile links.
  2. Change the slug and the posts in the same commit. Edit the author: line in every file under content/blog, and defaultAuthor in agentblog.config.ts, alongside the roster.
  3. Delete the example posts. They are the format specification, so read one first, but nothing depends on them existing.

The same trap has a quieter version. defaultAuthor in agentblog.config.ts ships as a placeholder that matches no record. It only bites the first post you write without an author in its frontmatter, which may be weeks later. Set it to a real slug while you are in the file.

The article text is not in the raw HTML

curl -s -A "GPTBot" https://yoursite.com/blog/your-post | grep "a distinctive sentence"

No match. Causes, in the order they occur:

Something in the render path is a client component. Look for 'use client' anywhere the article body is rendered. Only the table of contents and the share buttons are allowed to be client components, and both render meaningful content on the server first.

Content is mounted on interaction. An accordion or a tab set that renders its children only after a click. Use <details> instead: it is in the HTML whether or not it is open.

The route was not prerendered. Check that generateStaticParams returns every slug and is not sliced. agentblog doctor checks this by reading the syntax tree rather than searching the text.

You are looking at the element inspector. It shows the page after JavaScript has run. Use view source or curl.

The title is in the body instead of the head

curl -s -A "GPTBot" "$URL" | head -c 4000 | grep -q "<title>"

Fails. The page rendered dynamically and Next.js streamed the metadata, appending it to <body> instead of <head>.

Two fixes, and you want both. Prerender the post, so the metadata is in the initial HTML for everyone. Then set htmlLimitedBots, so the AI crawlers get a blocking render regardless:

npx agentblog@latest doctor --fix

Read the value it writes carefully. It repeats the entire Next.js default list after the AI crawlers, and that is not redundancy: setting this config replaces the default rather than extending it. Dropping the tail removes Googlebot, Bingbot, Applebot, Twitterbot, LinkedInBot, Slackbot, Discordbot, and Facebook's crawler from the same treatment, which trades an AI win for a live SEO and social preview regression.

Article prose has no typography at all

Not slightly off. One font size, no visible heading hierarchy, no list markers, while the rest of the site looks right. Cards and layout are fine, because those are utilities in the components. Only the article body is affected.

You did not import the stylesheet.

app/globals.css
@import 'tailwindcss';
@import '../styles/agentblog.css';

From src/app/globals.css the path is ../../styles/agentblog.css, because the file lands at the project root rather than next to app/.

Nothing catches this. It is not a doctor finding, the preflight check does not look at your stylesheet, the build succeeds, and every crawler check passes, because the HTML and the structured data are correct and only the presentation is missing.

A new post is not in the sitemap or the feed

sitemap.xml, robots.txt, and the social image routes are cached route handlers. Publishing a post and revalidating only its own path leaves all of them stale.

The publish webhook that ships with the blog revalidates the sitemap and the feed explicitly. If you wrote your own publish path, add them. agentblog doctor asserts it.

A published post 404s or renders a fallback

Your content source publishes without a rebuild, and the prerender step has not run since the last deploy, so the route was never built.

Check prerenderStrategy on your source. If publishing needs a rebuild it should be 'deploy-hook', and your config will then refuse to compile without a deployHook. The webhook fires the rebuild before it pings IndexNow, which is the correct order. Pinging first tells a crawler to fetch a URL that does not exist yet.

The crawler gets stale HTML right after publishing

revalidateTag takes a required second argument in Next.js 16, and 'max' on the publish path means you invalidated nothing useful. Use { expire: 0 }.

Also worth knowing: updateTag(tag) takes a single argument and works only in Server Actions. It throws in a route handler.

Social cards have no site name

View source on a built post and look for og:site_name. Missing means a segment defined its own social metadata without spreading the shared defaults.

Next.js merges metadata shallowly, so defining the object at all in a child segment discards the parent's entire object. Nothing errors, no validator complains, and the types are correct.

Spread the defaults from lib/metadata.ts in every segment that sets them. The same applies to the robots directives, where the loss costs you max-snippet and max-image-preview, which is what permits full quotations in AI answers.

The blog does not match the rest of the site

Most likely a dark-mode colour variant or a palette utility crept into a component you edited. The tokens already flip under .dark, so a dark-mode colour override re-hardcodes the thing the token abstracts.

npx agentblog@latest doctor

It reports palette utilities, colour literals, and dark-mode colour variants in the installed components. A warning rather than an error, because you may have done it deliberately.

If the prose specifically looks wrong, check styles/agentblog.css for hsl(var(--foreground)). Tailwind v4 stores complete oklch() values, so the correct form is var(--foreground) with no wrapper. The old form fails silently by producing an invalid colour that inherits.

Turbopack warns about dynamic filesystem access

Every build prints six of these, three from lib/preflight.ts and three from lib/sources/mdx.ts.

Warning: Dynamic filesystem access causes tracing of the whole project

These come from AgentBlog rather than from your code, and the build succeeds. Both files read the filesystem on purpose. The preflight check reads your next.config off disk to lint it, which is the entire reason it exists, and the path is your project root rather than a subfolder Turbopack can scope. The MDX source resolves the content directory from your config, so the path cannot be a literal it can fold.

The consequence is real but it is a size and deploy-time cost rather than a correctness one: more files get copied next to your server bundle. On a blog that is usually a slower deploy and a larger function. It matters if you are near a platform size limit.

If it is a real problem, trim the trace in your own next.config.ts rather than editing the installed files:

outputFileTracingExcludes: {
  '/**': ['./public/**/*'],
}

Exclude only what you are certain no route reads. Everything agentblog init put in outputFileTracingIncludes has to stay: the MDX source really does read content/blog/**, content/authors.json, and content/categories.json at runtime, on /blog and whenever the publish webhook regenerates the sitemap and the feed. Dropping one of them is the section below.

/blog returns a 500 and the post pages do not

The symptom is specific. Every post, category, and author page serves normally, /blog returns a server error, and the deployment logs carry:

Error [AgentBlogSourceError]: [agentblog:mdx] getAllPosts failed
  [cause]: Error: ENOENT: no such file or directory, open '/var/task/content/authors.json'

The post pages are prerendered at build time, when the whole repository is on disk. /blog reads searchParams for its ?page=N, so it renders on demand, inside a function that only carries the files the build traced. The file named in the error was not one of them.

Fix it by declaring every path your content source reads, not just the posts directory:

next.config.ts
outputFileTracingIncludes: {
  '/**': [
    './content/blog/**/*',
    './content/authors.json',
    './content/categories.json',
  ],
}

npx agentblog doctor --fix writes that for you, reading the paths out of your agentblog.config.ts. Two things about it are easy to get wrong by hand. The roster files sit beside the posts directory rather than inside it, so ./content/blog/**/* alone ships the posts and leaves the authors behind. And the key is /** rather than /blog/**, because sitemap.xml, feed.xml, /authors/[slug], and /api/publish read content too.

The image optimizer returns 400

Next.js 16 rejects any image quality not listed in your config, and the default list contains only 75. The blog uses 75 for cards and 90 for hero images.

images: {
  qualities: [75, 90]
}

agentblog doctor --fix writes this.

The build fails on a social image

The site social image has an 8MB limit and the Twitter variant has 5MB. Exceeding either fails the build rather than warning.

Read fonts and logos once at module scope rather than per request, or every generated image pays for the read.

The preflight warning will not go away

It is telling you something. Read the line: it names the missing setting.

If your config is genuinely correct and the check is wrong, set preflight: false in agentblog.config.ts. That is the supported way to silence it. Deleting the import from app/blog/layout.tsx is not, because then nothing tells you when a later edit breaks the config again. If you think the check is wrong, please open an issue: a false positive here is a bug that gets the import deleted in a lot of repositories.

IndexNow returns 403 or 422

  • 403: the key is invalid or the key file is missing. public/<key>.txt has to be served from your domain root, UTF-8, containing the key and nothing else. A key hosted at a subpath only authorises URLs under that subpath.
  • 422: the submitted URL does not match the host that owns the key.

Both look identical to success from a caller that only checks whether the request completed, which is why agentblog ping prints the code with its meaning attached.

agentblog init refuses to run

MessageMeaning
components.json not foundRun npx shadcn@latest init first. AgentBlog will not, because that command picks a design system
Tailwind v3 detectedNot supported. See the roadmap
app/blog already existsYou have a blog. --force overwrites it, and the conflicting paths are printed
Next.js 15 detectedThe product is built on APIs specific to Next.js 16

Undoing an install

npx agentblog@latest revert                # restore the last patch set
npx agentblog@latest revert --all          # replay every backup, back to the start
npx agentblog@latest uninstall             # reverse every patch, remove the AGENTS.md block
npx agentblog@latest uninstall --keep-env  # the same, but leave the two secrets in place

revert on its own restores only the most recent backup, so a second doctor --fix can be undone without also unwinding your init.

uninstall lists the files the registry wrote rather than deleting them, because by then some of them are yours.

On this page