{"record":{"id":"46e9d118596ae9df","repo":"withastro/astro","slug":"id-must-be-a-non-empty-string","errorCode":null,"errorMessage":"ID must be a non-empty string","messagePattern":"ID must be a non-empty string","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/astro/src/content/mutable-data-store.ts","lineNumber":409,"sourceCode":"\t\treturn {\n\t\t\tget: <TData extends Record<string, unknown> = Record<string, unknown>>(key: string) =>\n\t\t\t\tthis.get<DataEntry<TData>>(collectionName, key),\n\t\t\tentries: () => this.entries(collectionName),\n\t\t\tvalues: () => this.values(collectionName),\n\t\t\tkeys: () => this.keys(collectionName),\n\t\t\tset: ({\n\t\t\t\tid: key,\n\t\t\t\tdata,\n\t\t\t\tbody,\n\t\t\t\tfilePath,\n\t\t\t\tdeferredRender,\n\t\t\t\tdigest,\n\t\t\t\trendered,\n\t\t\t\tassetImports,\n\t\t\t\timageImports: incomingImageImports,\n\t\t\t}) => {\n\t\t\t\tif (!key) {\n\t\t\t\t\tthrow new Error(`ID must be a non-empty string`);\n\t\t\t\t}\n\t\t\t\tconst id = String(key);\n\t\t\t\tif (digest) {\n\t\t\t\t\tconst existing = this.get<DataEntry>(collectionName, id);\n\t\t\t\t\tif (existing && existing.digest === digest) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst foundAssets = new Set<string>(assetImports);\n\t\t\t\tconst imageImports: (string | number)[][] = [];\n\t\t\t\tconst seenImageImportPaths = new Set();\n\t\t\t\tconst recordImageImport = (imagePath: (string | number)[]) => {\n\t\t\t\t\tconst pathKey = JSON.stringify(imagePath);\n\t\t\t\t\tif (seenImageImportPaths.has(pathKey)) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tseenImageImportPaths.add(pathKey);\n\t\t\t\t\timageImports.push(imagePath);","sourceCodeStart":391,"sourceCodeEnd":427,"githubUrl":"https://github.com/withastro/astro/blob/e294953aa8aadd98d5be92e60a03037b05dbdfd4/packages/astro/src/content/mutable-data-store.ts#L391-L427","documentation":"Loaders persist entries through the scoped data store's `set()`. It coerces ids to string (numeric YAML ids are fine) but rejects falsy ones outright with a plain Error — an empty or missing id would corrupt the collection keyed by ''.","triggerScenarios":"A custom Loader calling `store.set({ id: raw.id, data })` where `raw.id` is undefined, null, 0-with-coercion... specifically '', undefined or null; e.g. API rows or object keys where the id field is blank.","commonSituations":"Writing a custom loader (API/CSV/headless CMS) without guarding ids; data where some rows legitimately lack ids; spreading defaults that turn missing ids into empty strings.","solutions":["Guard in your loader: skip, log, or throw a descriptive error when `!raw.id` before calling `store.set`.","Derive an id from another field (slugified title, hash, index) when the source id is blank.","Validate your data source once at load time instead of per-entry mid-sync so failures are actionable."],"exampleFix":"// before\nfor (const raw of rows) {\n  store.set({ id: raw.id, data: raw }); // raw.id can be '' or undefined\n}\n\n// after\nfor (const raw of rows) {\n  if (!raw.id) {\n    logger.warn(`Skipping row without id: ${JSON.stringify(raw)}`);\n    continue;\n  }\n  store.set({ id: raw.id, data: raw });\n}","handlingStrategy":"validation","validationCode":"for (const raw of rows) {\n  if (!raw.id || String(raw.id).trim() === '') {\n    logger.warn(`Skipping entry without id: ${JSON.stringify(raw)}`);\n    continue;\n  }\n  store.set({ id: raw.id, data: raw });\n}","typeGuard":"const hasUsableId = (raw: unknown): raw is { id: string } =>\n  typeof (raw as any)?.id === 'string' && (raw as any).id.length > 0;\n\nif (hasUsableId(raw)) store.set({ id: raw.id, data: raw });","tryCatchPattern":null,"preventionTips":["Validate ids at ingest in custom loaders; never pass source data through unguarded.","Numeric ids are fine (they are stringified) — only empty/missing ids throw.","Log skipped rows so silent data loss stays visible."],"tags":["content-collections","data-store","entry-id","custom-loader"],"backgroundTag":"missing-entry-id","analyzedSha":"e294953aa8aadd98d5be92e60a03037b05dbdfd4","analyzedAt":"2026-08-27T22:42:12.133Z","contentChangedAt":"2026-08-27T22:42:12.133Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}