{"record":{"id":"f3efe33926c4926a","repo":"withastro/astro","slug":"rss-you-can-only-glob-entries-within-src-pages","errorCode":null,"errorMessage":"[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/","messagePattern":"\\[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/","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/astro-rss/src/index.ts","lineNumber":138,"sourceCode":"\t\t\t\t\t\tmessage,\n\t\t\t\t\t\t`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.`,\n\t\t\t\t\t\t`Check your collection's schema, and visit https://docs.astro.build/en/recipes/rss/#generating-items for more info.`,\n\t\t\t\t\t].join('\\n');\n\t\t\t\t}\n\n\t\t\t\treturn message;\n\t\t\t}),\n\t\t].join('\\n'),\n\t);\n\tthrow formattedError;\n}\n\nexport function pagesGlobToRssItems(items: GlobResult): Promise<ValidatedRSSFeedItem[]> {\n\treturn Promise.all(\n\t\tObject.entries(items).map(async ([filePath, getInfo]) => {\n\t\t\tconst { url, frontmatter } = await getInfo();\n\t\t\tif (url === undefined || url === null) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[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/`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst parsedResult = rssSchema\n\t\t\t\t.refine((val) => val.title || val.description, {\n\t\t\t\t\tmessage: 'At least title or description must be provided.',\n\t\t\t\t\tpath: ['title', 'description'],\n\t\t\t\t})\n\t\t\t\t.safeParse({ ...frontmatter, link: url });\n\n\t\t\tif (parsedResult.success) {\n\t\t\t\treturn parsedResult.data;\n\t\t\t}\n\t\t\tconst formattedError = new Error(\n\t\t\t\t[\n\t\t\t\t\t`[RSS] ${filePath} has invalid or missing frontmatter.\\nFix the following properties:`,\n\t\t\t\t\t...parsedResult.error.issues.map((zodError) => zodError.message),\n\t\t\t\t].join('\\n'),","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/withastro/astro/blob/d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca/packages/astro-rss/src/index.ts#L120-L156","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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()."],"exampleFix":"// before\nimport { rss, pagesGlobToRssItems } from '@astrojs/rss';\nexport async function GET(context) {\n  return rss({\n    items: await pagesGlobToRssItems(import.meta.glob('../content/**/*.md')),\n  });\n}\n// after\nexport async function GET(context) {\n  return rss({\n    items: await pagesGlobToRssItems(import.meta.glob('./blog/*.{md,mdx}')),\n  });\n}","handlingStrategy":"validation","validationCode":"// Resolve every glob entry and keep only those with a URL before calling rss()\nconst entries = await Promise.all(\n  Object.entries(import.meta.glob('./blog/*.{md,mdx}')).map(async ([k, getInfo]) => {\n    const info = await getInfo();\n    return info.url ? { ok: true, key: k, url: info.url, frontmatter: info.frontmatter } : { ok: false, key: k };\n  }),\n);\nconst bad = entries.filter((e) => !e.ok).map((e) => e.key);\nif (bad.length) throw new Error(`Non-page files in RSS glob (no route URL): ${bad.join(', ')}`);","typeGuard":"// Narrow a glob result to page entries that expose a URL\nfunction isPageGlobEntry(info: any): info is { url: string; frontmatter: Record<string, unknown> } {\n  return info && typeof info === 'object' && typeof info.url === 'string';\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["rss","build","import-meta-glob","routing"],"backgroundTag":null,"analyzedSha":"d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca","analyzedAt":"2026-08-12T13:37:29.035Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}