koala73/worldmonitor · error · Error
empty sitemap: ${location}
Error message
empty sitemap: ${location} What it means
After extracting <loc> values from every entry of a fetched sitemap, getPublishedBatches throws this when the resulting `urls` array is empty — the sitemap document exists and parses into entries but contains no locations (or no entries at all), so there is nothing to submit to IndexNow.
Solutions
- Check why the sitemap generator produced no URLs — verify pages are eligible (not excluded by noindex/robots or build filters) and rerun the generator.
- Fetch the sitemap manually to confirm whether the deployment serves the real file or an empty/placeholder document; redeploy if stale.
- Purge the CDN/edge cache for the sitemap URL after regenerating.
- For the root sitemap specifically, ensure SITEMAP_INDEX_MEMBERS are generated so the index is never empty.
Example fix
// before (empty served sitemap) <?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"></urlset> // after <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <url><loc>https://example.com/</loc></url> </urlset>
Defensive patterns
Strategy: validation
Validate before calling
const xml = await res.text();
if (!/<(url|sitemap)>[\s\S]*<\/(url|sitemap)>/.test(xml)) {
throw new Error(`sitemap has no entries, skipping IndexNow submit: ${url}`);
} Try / catch
try {
const batches = await getPublishedBatches(origin);
} catch (err) {
if (err instanceof Error && err.message.startsWith('empty sitemap')) {
console.warn(`Sitemap is empty; nothing to submit: ${err.message}`);
return;
}
throw err;
} Prevention
- Assert in CI that the generated sitemap contains at least one URL (and the root index lists all SITEMAP_INDEX_MEMBERS).
- Verify the sitemap generator's exclusion filters (noindex, robots, build config) don't accidentally filter out every page.
- Check the deployed sitemap after each deploy as a smoke test before running SEO scripts against it.
- Purge CDN/edge cache so an empty placeholder never lingers after content exists.
When it happens
Trigger: Fetching `location` returns XML where the entry regex matches nothing (e.g. an empty `<urlset></urlset>` or `<sitemapindex></sitemapindex>`), or all matched candidates were HTML comments and were filtered out, leaving `urls.length === 0`.
Common situations: A fresh deploy publishes an empty sitemap before content is generated; the sitemap generator filtered out every URL (all pages excluded via robots/noindex); a cache or placeholder document is served instead of the real sitemap.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
- expected one location per sitemap entry: ${location}
- invalid sitemap location: ${location}
- published sitemap tree exceeds 50 documents
- ${location} returned ${response.status}, expected direct 200
- invalid sitemap document: ${location}
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/08cba864c042d489.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/seo-indexnow-submit.mjs:194
method: 'GET',
redirect: 'manual',
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
headers: { Accept: 'application/xml', 'User-Agent': USER_AGENT },
});
if (response.status !== 200) throw new Error(`${location} returned ${response.status}, expected direct 200`);
const source = (await response.text()).trim();
const root = /^(?:<\?xml[^?]*\?>\s*)?<(sitemapindex|urlset)\b[^>]*>([\s\S]*)<\/\1>\s*$/.exec(source);
if (!root) throw new Error(`invalid sitemap document: ${location}`);
const tag = root[1] === 'sitemapindex' ? 'sitemap' : 'url';
const entryPattern = new RegExp(`<!--[\\s\\S]*?-->|<${tag}>[\\s\\S]*?<\\/${tag}>`, 'g');
const entries = [...root[2].matchAll(entryPattern)].filter(([entry]) => !entry.startsWith('<!--'));
if (root[2].replace(entryPattern, '').trim()) throw new Error(`invalid sitemap entries: ${location}`);
const urls = entries.map(([entry]) => {
const locations = [...entry.matchAll(/<loc>\s*([^<]+?)\s*<\/loc>/g)];
if (locations.length !== 1) throw new Error(`expected one location per sitemap entry: ${location}`);
return decodeXml(locations[0][1].trim());
});
if (urls.length === 0) throw new Error(`empty sitemap: ${location}`);
if (location === `${origin}/sitemap.xml`
&& (root[1] !== 'sitemapindex' || urls.length !== SITEMAP_INDEX_MEMBERS.length
|| new Set(urls).size !== urls.length || urls.some(url => !SITEMAP_INDEX_MEMBERS.includes(url)))) {
throw new Error('published root sitemap members do not match the declared inventory');
}
if (root[1] === 'sitemapindex') {
pending.push(...urls);
} else {
for (const url of urls) {
const page = new URL(url);
if (page.protocol !== 'https:' || !hosts.has(page.host) || page.username || page.password || page.hash || page.pathname.endsWith('.xml')) {
throw new Error(`invalid published page URL: ${url}`);
}
pages.add(url);
}
}
}
for (const family of ['docs', 'blog']) {View on GitHub (pinned to 7d06c8633d)