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
- 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}'.
- Do not pass the glob result directly: map it to an explicit RSSFeedItem[] with your own link values and pass that array to rss().
- 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
- Only glob files under src/pages/ when passing import.meta.glob() to rss().
- Prefer building an explicit RSSFeedItem[] with your own link values to avoid route assumptions.
- In CI, assert the glob pattern resolves exclusively to page files.
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
- Invalid route ${file} — brackets are unbalanced
- Invalid route ${file} — rest parameter must be a standalone
- GetStaticPathsRequired
- InvalidGetStaticPathsReturn
- InvalidGetStaticPathsEntry
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/f3efe33926c4926a.
Report an issue: GitHub.