{"record":{"id":"d01bef4774abf678","repo":"withastro/astro","slug":"contentloaderreturnsinvalidid","errorCode":"ContentLoaderReturnsInvalidId","errorMessage":"The content loader for the collection **${collection}** returned an entry with an invalid `id`:\n${entry}","messagePattern":"The content loader for the collection \\*\\*(.+?)\\*\\* returned an entry with an invalid `id`:\n(.+?)","errorType":"error_code","errorClass":"AstroError","httpStatus":null,"severity":"error","filePath":"packages/astro/src/content/content-layer.ts","lineNumber":492,"sourceCode":") {\n\tconst unsafeData = await handler();\n\tconst parsedData = loaderReturnSchema.safeParse(unsafeData);\n\n\tif (!parsedData.success) {\n\t\tconst issue = parsedData.error.issues[0] as z.core.$ZodIssueInvalidUnion;\n\n\t\t// Due to this being a union, zod will always throw an \"Expected array, received object\" error along with the other errors.\n\t\t// This error is in the second position if the data is an array, and in the first position if the data is an object.\n\t\tconst parseIssue = Array.isArray(unsafeData) ? issue.errors[0] : issue.errors[1];\n\n\t\tconst error = parseIssue[0];\n\t\tconst firstPathItem = error.path[0];\n\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),","sourceCodeStart":474,"sourceCodeEnd":510,"githubUrl":"https://github.com/withastro/astro/blob/d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca/packages/astro/src/content/content-layer.ts#L474-L510","documentation":"`simpleLoader` (used when a collection loader is a function) validates the returned data against a schema that requires each entry to have a string `id`. If validation fails, Astro extracts the offending entry from the union parse issue and throws `ContentLoaderReturnsInvalidId` showing that entry, so the loader author can see exactly which record is malformed.","triggerScenarios":"A function-style collection loader returns an array or object whose entries fail the `{ id: string }` shape — e.g. entries where `id` is a number, missing, null, or an object.","commonSituations":"Fetching from a CMS whose items use `slug`/`_id` instead of `id`; transforming API responses without normalizing the id field; returning raw DB rows where the PK is named differently.","solutions":["Map each returned item to an object with a string `id` before returning from the loader.","If returning an object map, use the key as the id (do not also set a conflicting `id`).","Log the loader's raw return value once to inspect the shape against the schema."],"exampleFix":"// before\nconst loader = () => fetch('/api/posts').then(r => r.json());\n// after\nconst loader = () => fetch('/api/posts').then(r => r.json()).then(arr => arr.map(p => ({ id: String(p.slug), ...p })));","handlingStrategy":"validation","validationCode":"const out = await loader();\nconst entries = Array.isArray(out) ? out : Object.values(out);\nif (!entries.every(e => e && typeof e.id === 'string')) {\n  throw new Error('Every entry must have a string id');\n}","typeGuard":"function entriesHaveStringId(data): data is Array<{ id: string }> {\n  return Array.isArray(data) && data.every(e => e && typeof e.id === 'string');\n}","tryCatchPattern":null,"preventionTips":["Normalize source records to a string `id` inside the loader.","Map foreign id fields (slug, _id, key) to `id` before returning.","Add a unit test asserting every entry has a string id."],"tags":["content-collections","loader","validation","data-shape"],"backgroundTag":null,"analyzedSha":"d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca","analyzedAt":"2026-08-12T13:37:29.035Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}