{"record":{"id":"0097d5e8655cd6b5","repo":"withastro/astro","slug":"invalidcontententryslugerror","errorCode":"InvalidContentEntrySlugError","errorMessage":"${String(collection)} → ${String(entryId)} has an invalid slug. `slug` must be a string.","messagePattern":"(.+?) → (.+?) has an invalid slug\\. `slug` must be a string\\.","errorType":"error_code","errorClass":"AstroError","httpStatus":null,"severity":"error","filePath":"packages/astro/src/content/utils.ts","lineNumber":149,"sourceCode":"export type CollectionConfig = ContentConfig['collections'][string];\n\ntype EntryInternal = { rawData: string | undefined; filePath: string };\n\nexport function parseEntrySlug({\n\tid,\n\tcollection,\n\tgeneratedSlug,\n\tfrontmatterSlug,\n}: {\n\tid: string;\n\tcollection: string;\n\tgeneratedSlug: string;\n\tfrontmatterSlug?: unknown;\n}) {\n\ttry {\n\t\treturn z.string().default(generatedSlug).parse(frontmatterSlug);\n\t} catch {\n\t\tthrow new AstroError({\n\t\t\t...AstroErrorData.InvalidContentEntrySlugError,\n\t\t\tmessage: AstroErrorData.InvalidContentEntrySlugError.message(collection, id),\n\t\t});\n\t}\n}\n\nexport async function getEntryData<\n\tTInputData extends Record<string, unknown> = Record<string, unknown>,\n\tTOutputData extends TInputData = TInputData,\n>(\n\tentry: {\n\t\tid: string;\n\t\tcollection: string;\n\t\tunvalidatedData: TInputData;\n\t\t_internal: EntryInternal;\n\t},\n\tcollectionConfig: CollectionConfig,\n\tshouldEmitFile: boolean,","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/withastro/astro/blob/e294953aa8aadd98d5be92e60a03037b05dbdfd4/packages/astro/src/content/utils.ts#L131-L167","documentation":"While syncing markdown/MDX entries, Astro validates the optional frontmatter slug with z.string(), falling back to the filename-generated slug when absent. If a slug field is present but not a string — a number, boolean, list, or unquoted YAML date — parsing fails and the error names the offending collection and entry id.","triggerScenarios":"Frontmatter such as slug: 123 (number), slug: [a, b] (list), slug: true, or slug: 2024-05-01 which YAML parses as a date; any non-string value the CMS or a script writes into the slug field.","commonSituations":"Numeric or date-like slugs left unquoted in YAML; CMS exports that emit typed values; frontmatter templates that default slug to null.","solutions":["Open the named entry and make the slug a quoted string: slug: \"123\".","Remove the slug field entirely to fall back to the generated filename slug.","If a script or CMS writes the frontmatter, stringify the slug value before writing."],"exampleFix":"# before\n---\nslug: 2024-05-01\ntitle: My post\n---\n\n# after\n---\nslug: \"2024-05-01\"\ntitle: My post\n---","handlingStrategy":"validation","validationCode":"// Pre-build check: frontmatter slug must be a quoted string when present\nimport { readdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nfor (const file of await readdir('src/content', { recursive: true })) {\n  if (!String(file).endsWith('.md')) continue;\n  const lines = (await readFile(join('src/content', file), 'utf8')).split('\\n');\n  const slugLine = lines.find((l) => l.startsWith('slug:'));\n  if (!slugLine) continue;\n  const value = slugLine.slice(5).trim();\n  const quoted = value.startsWith('\"') || value.startsWith(\"'\");\n  if (!quoted) console.error(`slug needs quotes in ${file}: ${value}`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always quote slug values in YAML, especially numeric or date-like ones.","When generating frontmatter programmatically, coerce slug to String() before writing.","Add a content lint step to CI that validates frontmatter types."],"tags":["frontmatter","markdown","slug","content-collections","yaml","validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"e294953aa8aadd98d5be92e60a03037b05dbdfd4","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}