withastro/astro · error · AstroError
ContentLoaderInvalidDataError
ContentLoaderInvalidDataError
Error message
**${collection}** entry is missing an ID.
Entry missing ID:
${raw} What it means
After `simpleLoader` validates the schema, it iterates the returned array and explicitly checks each `raw.id`. If an entry has a falsy `id`, it throws `ContentLoaderInvalidDataError` with the offending entry serialized. This is a defensive check beyond schema validation to produce a clear, actionable message.
Source
Thrown at packages/astro/src/content/content-layer.ts:505
const entry = Array.isArray(unsafeData)
? unsafeData[firstPathItem as number]
: unsafeData[firstPathItem as string];
throw new AstroError({
...AstroErrorData.ContentLoaderReturnsInvalidId,
message: AstroErrorData.ContentLoaderReturnsInvalidId.message(context.collection, entry),
});
}
const data = parsedData.data;
context.store.clear();
if (Array.isArray(data)) {
for (const raw of data) {
if (!raw.id) {
throw new AstroError({
...AstroErrorData.ContentLoaderInvalidDataError,
message: AstroErrorData.ContentLoaderInvalidDataError.message(
context.collection,
`Entry missing ID:\n${JSON.stringify({ ...raw, id: undefined }, null, 2)}`,
),
});
}
const item = await context.parseData({ id: raw.id, data: raw });
context.store.set({ id: raw.id, data: item });
}
return;
}
if (typeof data === 'object') {
for (const [id, raw] of Object.entries(data)) {
if (raw.id && raw.id !== id) {
throw new AstroError({
...AstroErrorData.ContentLoaderInvalidDataError,
message: AstroErrorData.ContentLoaderInvalidDataError.message(View on GitHub (pinned to d081033d5f)
Solutions
- Filter or repair entries before returning them so every entry has a non-empty string id.
- Generate a stable id (e.g. from a slug or hash) when the source lacks one.
- Skip entries without an id explicitly if they should not be indexed.
Example fix
// before
const loader = () => fetch('/api/posts').then(r => r.json());
// after
const loader = () => fetch('/api/posts').then(r => r.json()).then(arr => arr.filter(p => p.id).map(p => ({ id: p.id, ...p }))); Defensive patterns
Strategy: validation
Validate before calling
const out = await loader();
if (Array.isArray(out)) {
for (const raw of out) if (!raw.id) throw new Error('Entry missing id');
} Type guard
function everyEntryHasId(arr) {
return arr.every(e => !!e?.id);
} Prevention
- Filter out entries without ids before returning from the loader.
- Generate stable ids when the source lacks them.
- Test loaders against edge-case inputs (null id, missing field).
When it happens
Trigger: A function loader returns an array where one or more entries have `id: undefined`, `id: null`, `id: ''`, or no `id` at all.
Common situations: Some CMS items lack a slug; partial DB rows where the id column is null; conditional `id` assignment that drops the field for certain records.
Related errors
- ContentLoaderReturnsInvalidId
- ID must be a non-empty string
- File path must be relative to the site root. Got: ${filePath
- Live content collections must be defined in "src/live.config
- Collection loader for ${name} does not have a load method
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/9efb321d770fba97.
Report an issue: GitHub.