Skip to content

A local incremental astro build does not re-validate posts when the taxonomy changes — a removed term surfaces as a render crash, not a schema error #191

Description

@hotlong

Found while ablating #185 (PR #190). Filed unassigned, not fixed under that card: #185 is scoped to the slug sets in scripts/content-lint.mjs, and this is a different defect class — how the build behaves when the schema's source changes underneath an unchanged post. CI is not affected; this is a local-build effect only.

What happens

Astro 5's content-layer data store for this project lives at node_modules/.astro/data-store.json, and the glob loader keys cached entries on the entry file's own content. src/content.config.ts builds its enums from the taxonomy (slugsByGroup -> src/lib/term-data.ts), so the schema has a second input that the cache key does not cover: change the taxonomy without touching a post, and that post is not re-validated.

Reproduced on origin/main @ 435ff61, twice, with a term added to RAW_TERMS, used by a post, then removed while the post kept it:

  • Warm store (the previous build had cached the entry): astro build exits 1, but at render, not validation —

    02:34:25   ├─ /en/blog/ai-agent-workbench/index.htmlCannot read properties of undefined (reading 'group')
      Stack trace:
        at termSlugPath (.../dist/chunks/posts_CSTpWnFb.mjs:1464:15)
        at .../dist/pages/_lang_/blog/_---slug_.astro.mjs:74:29
    
  • Cold store (rm -rf node_modules/.astro): the schema does its job and says exactly what is wrong —

    [InvalidContentEntryDataError] blog → ai-agent-workbench data does not match collection schema.
      topic: Invalid enum value. Expected 'ai-agents' | 'app-building' | ... , received 'ablation-probe'
    

The crash is termHref(locale, termBySlug(data.topic)!) in src/layouts/BlogPost.astro (lines 91 / 122 and the termBySlug(slug)! calls under them) reaching termSlugPath with undefined. Those non-null assertions are not the bug — they are correct contract-first code, trusting the producer. The bug is that the producer was skipped.

Why it is worth recording

The failure names a compiled chunk and a property called group. Nothing in it points at the post, the field, or the taxonomy edit that caused it, so someone renaming a term locally gets a message that reads like a code bug in posts.ts. The one-line correct diagnosis (rm -rf node_modules/.astro) is not discoverable from the output.

Two things bound the severity, which is why this is a finding and not queued work:

  • CI checks out fresh, so the store is always cold there — the schema error is what a PR actually gets.
  • After PR content-lint: derive all four frontmatter term sets from the taxonomy #190, pnpm content:lint reports Invalid topic: <slug> on this exact state (leg C1 of that PR's ablation) regardless of any cache, and pnpm build runs content:lint --published before astro build, so the local pnpm build path is already covered. Only a bare astro build / astro dev hits the confusing form.

Possible shapes, if it is ever worth acting on

  • Leave it. The exposure is one command, and content:lint already gives the legible message.
  • Have astro.config.mjs fold a digest of the taxonomy into something the loader keys on, so a taxonomy edit invalidates blog entries.
  • Give src/lib/terms.ts a termBySlugOrThrow(slug) that names the slug and the file, so the render path fails with an attributable message instead of a property read on undefined — a smaller change that does not try to outsmart the cache.

Re-check

cd <worktree> && node scripts/... # add a topic term to src/lib/term-data.ts, point a post at it
pnpm exec astro build            # green, hub emitted
# remove the term from src/lib/term-data.ts, leave the post
pnpm exec astro build            # exit 1, render crash in termSlugPath
rm -rf node_modules/.astro && pnpm exec astro build   # exit 1, InvalidContentEntryDataError

Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions