半路手记 · 踩坑实录
报错
新写了一篇博客文章,放进内容目录,跑构建。秒挂。
[InvalidContentEntryDataError] blog → my-new-post data does not match collection schema.
summary: Required
报错信息说:这篇文章的数据不符合集合的 schema,缺少 summary 字段。
看起来很明确对吧?但这条报错把我带偏了半小时。
排查
我的第一反应是打开新文章看 frontmatter:
---
title: 我的新文章
date: 2026-07-14
tags: [随笔, 经验]
---
确实没有 summary。但是——我翻了翻其他文章,好几篇也没有 summary 字段,它们不是好好地构建成功了吗?
于是我开始在其他方向上找原因:是不是 tags 的问题?日期格式的问题?文件名有非法字符?
反复对比新旧文章的 frontmatter,逐字段排查,花了半小时。
根因
最终我打开了 content.config.ts——Astro 定义内容集合 schema 的配置文件。
const blogSchema = z.object({
title: z.string(),
date: z.coerce.date(),
summary: z.string(),
tags: z.array(z.string()).default([]),
});
schema 里清清楚楚写着:summary: z.string()——必填。
那为什么旧文章没有 summary 也能通过?
回去仔细看旧文章的 frontmatter——它们确实有 summary 字段,只是我之前扫一眼的时候忽略了。它们长这样:
---
title: 某某文章
date: 2026-07-10
summary: 一句话描述这篇文章的内容
tags: [技术, 经验]
---
我以为旧文章没有 summary,实际上是我看漏了。用「其他文件能过」来反推规则,本身就是不可靠的调试方法。
真正该做的第一步
遇到这类报错,正确的做法不是看报错信息然后去对比其他文件,而是:
直接打开 schema 定义文件,看规则本身。
报错信息告诉你「不匹配」,但不告诉你「应该是什么」。其他文件的 frontmatter 是规则的实例,不是规则本身。只有 schema 定义才是唯一的真相源。
这一步看似显而易见,但在实际操作中很容易被跳过。因为人的本能是「对比」——拿一个能用的和一个不能用的放在一起看,找不同。这在大多数调试场景下有效,但在 schema 校验的场景下会失效:你用来对比的「好的样本」可能本身就有你不知道的字段。
调试清单
后来我给自己列了一套针对 Astro 构建报错的排查流程:
1. 看报错信息,定位文件和集合。
报错会告诉你哪个文件、哪个集合出了问题。这是唯一的确定信息。
2. 打开 content.config.ts,看对应集合的 schema。
哪些字段是必填的(没有 .optional() 或 .default()),哪些有默认值。这一步比对比其他文件可靠得多。
3. 改完立刻构建。
不要攒着。新增内容后立刻 npm run build,报错越早出现越好修。
4. 如果报错信息不够明确,开 verbose 模式。
npm run build -- --verbose
Astro 的 verbose 模式会打印更详细的校验信息,告诉你是哪个字段缺失还是类型不匹配。
一个隐蔽的坑
这里还有一个值得注意的点:Astro 在构建时会校验集合里的所有文件,不只是你新加的那一篇。
这意味着:如果某篇旧文章也缺了必填字段,但你上次构建之后没碰过它,那个错误不会在那次构建里出现。它会藏在你下一次构建的报错里——而你可能误以为是新文件导致的问题。
在这个 case 里,新文章确实是直接原因。但在更大的项目里,这种「延迟暴露」的 schema 错误很容易造成误判。你以为是新代码引入了 bug,其实是旧代码一直在违规,只是还没被构建扫到。
一句话
报错说 schema 不匹配,第一反应不是看其他文件的 frontmatter,是打开 schema 定义文件。别从实例推规则,去看规则本身。