{"record":{"id":"9efb321d770fba97","repo":"withastro/astro","slug":"contentloaderinvaliddataerror","errorCode":"ContentLoaderInvalidDataError","errorMessage":"**${context.collection}** entry is missing an ID.\nEntry missing ID:\n${JSON.stringify({ ...raw, id: undefined }, null, 2)}","messagePattern":"\\*\\*(.+?)\\*\\* entry is missing an ID\\.\nEntry missing ID:\n(.+?), null, 2\\)\\}","errorType":"error_code","errorClass":"AstroError","httpStatus":null,"severity":"error","filePath":"packages/astro/src/content/content-layer.ts","lineNumber":505,"sourceCode":"\n\t\tconst entry = Array.isArray(unsafeData)\n\t\t\t? unsafeData[firstPathItem as number]\n\t\t\t: unsafeData[firstPathItem as string];\n\n\t\tthrow new AstroError({\n\t\t\t...AstroErrorData.ContentLoaderReturnsInvalidId,\n\t\t\tmessage: AstroErrorData.ContentLoaderReturnsInvalidId.message(context.collection, entry),\n\t\t});\n\t}\n\n\tconst data = parsedData.data;\n\n\tcontext.store.clear();\n\n\tif (Array.isArray(data)) {\n\t\tfor (const raw of data) {\n\t\t\tif (!raw.id) {\n\t\t\t\tthrow new AstroError({\n\t\t\t\t\t...AstroErrorData.ContentLoaderInvalidDataError,\n\t\t\t\t\tmessage: AstroErrorData.ContentLoaderInvalidDataError.message(\n\t\t\t\t\t\tcontext.collection,\n\t\t\t\t\t\t`Entry missing ID:\\n${JSON.stringify({ ...raw, id: undefined }, null, 2)}`,\n\t\t\t\t\t),\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst item = await context.parseData({ id: raw.id, data: raw });\n\t\t\tcontext.store.set({ id: raw.id, data: item });\n\t\t}\n\t\treturn;\n\t}\n\tif (typeof data === 'object') {\n\t\tfor (const [id, raw] of Object.entries(data)) {\n\t\t\tif (raw.id && raw.id !== id) {\n\t\t\t\tthrow new AstroError({\n\t\t\t\t\t...AstroErrorData.ContentLoaderInvalidDataError,\n\t\t\t\t\tmessage: AstroErrorData.ContentLoaderInvalidDataError.message(","sourceCodeStart":487,"sourceCodeEnd":523,"githubUrl":"https://github.com/withastro/astro/blob/52e6c34790cc8ac4e69e6135ace06049867e5c4a/packages/astro/src/content/content-layer.ts#L487-L523","documentation":"After a function-style loader's array passes schema validation, Astro walks it and requires each entry to have a truthy `id`. An empty-string id (`''`) is a valid string for zod but is falsy, so it triggers ContentLoaderInvalidDataError with the offending entry printed with `id: undefined`.","triggerScenarios":"A function-style loader returning an array where some entry has `id: ''` — e.g. a blank CSV column, a slugify step producing an empty slug from a title with only special characters, or API rows with missing ids defaulted to empty string.","commonSituations":"CSV data files with blank id columns; custom slug generators that strip all characters; upstream data where optional ids come back as empty strings instead of null.","solutions":["Filter or skip blank-id entries inside your loader before returning: `rows.filter((r) => r.id && r.id.trim())`.","Generate an id from another field (title, hash, index) when the source id is blank: `id: p.id || slugify(p.title)`.","Trim ids at ingest so whitespace-only ids do not slip through: `id: String(p.id).trim()`."],"exampleFix":"// before\nloader: async () => {\n  const rows = await parseCsv('data.csv'); // row: { id: '', title: 'x' }\n  return rows;\n},\n\n// after\nloader: async () => {\n  const rows = await parseCsv('data.csv');\n  return rows\n    .filter((r) => r.id && r.id.trim().length > 0)\n    .map((r) => ({ ...r, id: r.id.trim() }));\n},","handlingStrategy":"validation","validationCode":"const hasAllIds = (rows: Array<Record<string, any>>) =>\n  rows.every((r) => typeof r.id === 'string' && r.id.trim().length > 0);\n\nif (!hasAllIds(rows)) {\n  const bad = rows.findIndex((r) => !(typeof r.id === 'string' && r.id.trim()));\n  throw new Error(`Row ${bad} has a blank id: ${JSON.stringify(rows[bad])}`);\n}","typeGuard":"const hasNonEmptyId = (e: unknown): e is { id: string } =>\n  typeof (e as any)?.id === 'string' && (e as any).id.length > 0;","tryCatchPattern":"try {\n  await loader({ ...context } as any);\n} catch (err) {\n  if ((err as any)?.code === 'ContentLoaderInvalidDataError' && /missing an ID/.test(err.message)) {\n    // message embeds the offending entry; fix the source row and re-run\n  } else throw err;\n}","preventionTips":["Trim and check ids when ingesting CSV/API data instead of trusting them to be non-empty.","Generate fallback ids (slugified title, content hash) rather than shipping empty strings.","Add a data-quality test over your source files in CI so blank ids fail before Astro sees them."],"tags":["content-collections","content-layer","loader","entry-id"],"backgroundTag":"missing-entry-id","analyzedSha":"52e6c34790cc8ac4e69e6135ace06049867e5c4a","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}