withastro/astro · error · Error

[RSS] You can only glob entries within 'src/pages/' when pas

Error message

[RSS] You can only glob entries within 'src/pages/' when passing import.meta.glob() directly. Consider mapping the result to an array of RSSFeedItems. See the RSS docs for usage examples: https://docs.astro.build/en/recipes/rss/

What it means

Thrown by pagesGlobToRssItems() while iterating an import.meta.glob() result passed straight to rss(). Each globbed entry's loader resolves to { url, frontmatter }; only files inside src/pages/ get an Astro-generated route URL. When url is undefined/null the file is not a page route, so the helper cannot build an RSS item and aborts.

Source

Thrown at packages/astro-rss/src/index.ts:138

						message,
						`The \`items\` property requires at least the \`title\` or \`description\` key. They must be properly typed, as well as \`pubDate\` and \`link\` keys if provided.`,
						`Check your collection's schema, and visit https://docs.astro.build/en/recipes/rss/#generating-items for more info.`,
					].join('\n');
				}

				return message;
			}),
		].join('\n'),
	);
	throw formattedError;
}

export function pagesGlobToRssItems(items: GlobResult): Promise<ValidatedRSSFeedItem[]> {
	return Promise.all(
		Object.entries(items).map(async ([filePath, getInfo]) => {
			const { url, frontmatter } = await getInfo();
			if (url === undefined || url === null) {
				throw new Error(
					`[RSS] You can only glob entries within 'src/pages/' when passing import.meta.glob() directly. Consider mapping the result to an array of RSSFeedItems. See the RSS docs for usage examples: https://docs.astro.build/en/recipes/rss/`,
				);
			}
			const parsedResult = rssSchema
				.refine((val) => val.title || val.description, {
					message: 'At least title or description must be provided.',
					path: ['title', 'description'],
				})
				.safeParse({ ...frontmatter, link: url });

			if (parsedResult.success) {
				return parsedResult.data;
			}
			const formattedError = new Error(
				[
					`[RSS] ${filePath} has invalid or missing frontmatter.\nFix the following properties:`,
					...parsedResult.error.issues.map((zodError) => zodError.message),
				].join('\n'),

View on GitHub (pinned to d081033d5f)

Solutions

  1. Restrict the glob so it only matches page files under src/pages/, e.g. from src/pages/blog/ use import.meta.glob('./blog/*.{md,mdx}') (relative) or './**/*.{md,mdx}'.
  2. Do not pass the glob result directly: map it to an explicit RSSFeedItem[] with your own link values and pass that array to rss().
  3. Confirm every matched file actually renders as a route (has a URL) before relying on pagesGlobToRssItems().

Example fix

// before
import { rss, pagesGlobToRssItems } from '@astrojs/rss';
export async function GET(context) {
  return rss({
    items: await pagesGlobToRssItems(import.meta.glob('../content/**/*.md')),
  });
}
// after
export async function GET(context) {
  return rss({
    items: await pagesGlobToRssItems(import.meta.glob('./blog/*.{md,mdx}')),
  });
}
Defensive patterns

Strategy: validation

Validate before calling

// Resolve every glob entry and keep only those with a URL before calling rss()
const entries = await Promise.all(
  Object.entries(import.meta.glob('./blog/*.{md,mdx}')).map(async ([k, getInfo]) => {
    const info = await getInfo();
    return info.url ? { ok: true, key: k, url: info.url, frontmatter: info.frontmatter } : { ok: false, key: k };
  }),
);
const bad = entries.filter((e) => !e.ok).map((e) => e.key);
if (bad.length) throw new Error(`Non-page files in RSS glob (no route URL): ${bad.join(', ')}`);

Type guard

// Narrow a glob result to page entries that expose a URL
function isPageGlobEntry(info: any): info is { url: string; frontmatter: Record<string, unknown> } {
  return info && typeof info === 'object' && typeof info.url === 'string';
}

Prevention

When it happens

Trigger: Passing the raw import.meta.glob('./**/*.md') result to rss(), or a glob that matches files outside src/pages/ (e.g. src/content/, layouts, partials, or non-route files). Each loader whose getInfo() returns url === undefined triggers it.

Common situations: Globbing src/content/ content-collection Markdown by mistake; globbing src/pages/ but the pattern also catches layout/component files; mixing .mdoc content entries that never become routes.

Related errors


AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12). Data as JSON: /api/errors/f3efe33926c4926a. Report an issue: GitHub.