Ask
121

astro 5 getCollection returns an empty array after moving my blog to glob() Astro

Upgraded a 60-post blog from Astro 4 to 5.3. Moved the collection to the content layer loader:

const blog = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './content/blog' }),
  schema: z.object({ title: z.string(), date: z.coerce.date() })
});

Files are in src/content/blog/. getCollection('blog') returns []. No error, no warning. Build succeeds and produces an index page with zero entries, which is the worst possible failure mode.

Where do you even start debugging a loader that quietly returns nothing?

10 answers Share
Report

Answering anonymously — a moderator will review it first.

  • @edge_runtime_bo · 2w ago · 3 replies

    base resolves from the project root, not from src/content. Your files are in src/content/blog and you pointed it at ./content/blog, so it globbed a directory that doesn't exist and matched zero files. No error because an empty glob is perfectly legal.

    loader: glob({ pattern: '**/*.md', base: './src/content/blog' })
    

    Two follow-ups worth doing while you're there:

    • delete .astro/ and re-run. The content store is cached and a stale one will keep serving you an empty collection after you've fixed the path, which is how people conclude the fix didn't work
    • put if (posts.length === 0) throw new Error('no posts') in your index page. This will happen again, and a build that silently ships an empty blog is far worse than a red build
    139
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
    • @rubberduck_rae · 2w ago

      That was it. And yes, I had to nuke .astro/ before it took — spent ten minutes convinced the fix hadn't worked.

      38
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
    • @tomato_tobin · 2w ago

      The .astro/ cache has cost me more time than any actual Astro bug. First thing to try on anything content-layer shaped.

      22
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
  • @drainfield_dez · 2w ago

    General technique for silent loaders: run astro sync, then look in .astro/ at the data store. If your entries aren't in there at all, it's a loading problem — path, pattern, extension. If they're in there but getCollection gives you nothing, it's a filter or a schema issue.

    Also worth knowing that in 5.x a schema parse failure is loud, so "quiet and empty" almost always means the glob matched nothing.

    44
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @muslin_mira · 2w ago · 2 replies

    Since you're mid-migration, the next one that gets people: entries no longer have slug, they have id, and id is the path relative to base without the extension. If your routes used entry.slug they're now undefined and you get a set of /blog/undefined pages that also build perfectly fine.

    31
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
    • @rubberduck_rae · 2w ago

      Yep, hit that about ten minutes after fixing the glob. Two silent failures in one migration, which is a good hit rate.

      17
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
  • @cookie_domain_al · 2w ago

    Unrelated to the bug but while you're in there: if the markdown genuinely lives in src/content/, consider moving it out to a top-level content/. The content layer doesn't care where the files are any more, and keeping them out of src/ stops the dev server doing a full reload every time you fix a typo in a post.

    19
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @litercount_lou · 2w ago · 2 replies

    is z.coerce.date() actually working for you? mine kept giving Invalid Date on frontmatter dates without quotes

    18
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
    • @edge_runtime_bo · 2w ago

      z.coerce.date() handles a real Date object fine. What breaks people is an unquoted 2026-03-04 09:00, which YAML parses into a Date in the machine's local time, so you get an off-by-a-timezone rather than an error. Quote your dates in frontmatter and it goes away.

      15
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
  • @flux_and_solder · 2w ago

    Add a smoke test. One Vitest file that calls getCollection and asserts length > 0 catches this whole class of thing forever and takes about four minutes to write.

    8
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report