HSHSKY Lab
workflows5 min read

How a Missing Slash Broke My Sitemap (and How Claude Code Found It)

Google Search Console flagged 11 URLs in my sitemap as 'disallowed.' Here's the one-character config bug that caused it, and the short Claude Code session that tracked it down.

In-Article Ad — Replace with ad code after approval

This morning I opened Google Search Console for this site and saw a warning I hadn't seen before: "Sitemap is readable, but has errors." Eleven URLs were marked as disallowed. Here's the actual bug, and how I went from screenshot to fixed-and-deployed in one short session.

The Warning

The error list showed URLs like this:

https://hskylab.comtutorials/
https://hskylab.comcompare/
https://hskylab.comworkflows/

Google Search Console showing the sitemap error with malformed URLs missing a slash after .com

Look closely and the bug is obvious: there's no / between .com and the path. hskylab.comtutorials isn't a valid URL — it's hskylab.com and tutorials/ jammed together.

Handing It to Claude Code

I pasted the screenshot into Claude Code and asked it to fix it. No other context — just the image.

It found src/app/sitemap.ts, where every entry is built like this:

{ url: `${SITE.url}tutorials/`, ... },
{ url: `${SITE.url}compare/`, ... },
{ url: `${SITE.url}workflows/`, ... },

Then it traced SITE.url back to src/lib/constants.ts:

export const SITE = {
  url: "https://hskylab.com", // no trailing slash
  // ...
} as const;

"https://hskylab.com" + "tutorials/" = "https://hskylab.comtutorials/". That's the entire bug — a missing trailing slash on one constant, multiplied across every templated URL in the site.

The Fix

The obvious fix is adding a / inside each template string — but Claude Code pointed out that ${SITE.url} was used the same broken way in 16 places: the sitemap, the four dynamic article pages (compare/[slug], news/[slug], tutorials/[slug], workflows/[slug]), and the breadcrumb URLs in ArticleLayout.tsx.

Instead of patching 16 spots, the fix was to change the constant itself:

- url: "https://hskylab.com",
+ url: "https://hskylab.com/",

That one change fixes all 16 — but it also breaks 3 other places that were written correctly for the old (no-slash) convention, where the code already added its own leading slash:

- image: image || `${SITE.url}/images/og-default.png`,
+ image: image || `${SITE.url}images/og-default.png`,

Three lines like that, across ArticleSchema.tsx and OrganizationSchema.tsx, needed the leading slash removed so they wouldn't become https://hskylab.com//images/....

Net result: 1 constant changed, 3 small adjustments, 0 changes to the 16 broken templates.

Verifying Before Pushing

Tip

Don't trust that a one-line config change "looks right" in a diff — rebuild and inspect the actual generated output.

I ran a full production build and opened the generated sitemap.xml directly:

<url><loc>https://hskylab.com/</loc></url>
<url><loc>https://hskylab.com/tutorials/</loc></url>
<url><loc>https://hskylab.com/compare/</loc></url>
<url><loc>https://hskylab.com/workflows/</loc></url>

Every URL now has the slash in the right place. Then: commit, push, deploy.

Why This Bug Was Invisible Until Now

The site had been live for weeks with this bug, and nothing looked broken. Here's why.

Every link a visitor actually clicks — nav links, article links, footer links — is a relative path like /tutorials/, rendered through Next.js's <Link>. Those never touch SITE.url at all, so navigation worked perfectly the whole time.

SITE.url only gets concatenated into absolute URLs: the sitemap, JSON-LD structured data, canonical tags, Open Graph URLs. Those aren't things you click through during normal testing — they're things crawlers and social previews read. The bug was sitting in metadata the whole time, quietly telling Google that /tutorials/, /compare/, and /workflows/ lived at a domain that doesn't exist.

Warning

If you keep a SITE_URL / baseUrl constant, decide on a trailing-slash convention on day one and grep for every place it's concatenated with a path. TypeScript won't catch this — it's just string concatenation, and both the broken and working versions type-check fine.

What's Next

The fix is pushed. The next step is going back to Search Console and using "Validate Fix" on the sitemap, which kicks off a recrawl — that part isn't instant, so I'll know in a few days whether all 11 URLs clear.

FAQ

Why didn't this break my site's navigation or styling?

Because internal navigation uses relative paths (/tutorials/), not the SITE.url constant. Only generated absolute URLs — sitemap entries, JSON-LD, canonical and Open Graph tags — were affected. Visually, nothing was wrong.

How do I check if my own sitemap has this bug?

Visit /sitemap.xml on your site and scan for .com (or .dev, .io, etc.) immediately followed by a path segment with no / — like example.comblog/. If you find one, check how your base-URL constant is concatenated with paths.

Is this specific to Next.js?

No. Any project that keeps a base-URL constant and builds absolute URLs with string templates can hit this. The Next.js-specific part here was just where the bug lived — app/sitemap.ts and the page-level metadata.

Bottom Line

One missing character, sitting quietly in a config file, telling Google that a third of my site's URLs lived on a domain that doesn't exist. It took longer to write this post than it took Claude Code to find the bug, trace it across 19 call sites, and ship a fix that didn't break the 3 places that were already correct.

In-Article Ad — Replace with ad code after approval

Tags

claude codeseonext.jssitemapdebuggingworkflow
H

Written by HSKY

Developer writing about AI coding tools — Claude Code, Cursor, agents, and the workflows that make them work.