withastro/astro · warning

**${collection}** contains multiple entries with the same sl

Error message

**${collection}** contains multiple entries with the same slug: `${slug}`. Slugs must be unique.

Entries: 
- ${preExisting}
- ${alsoFound}

What it means

Two items in a file()-loaded data array resolved to the same id (from the item's `id` or `slug` field). The DuplicateContentEntrySlugError message names the collection and the colliding id; depending on the astro.config option prerenderConflictBehavior (default 'warn') this either throws an AstroError, logs this warning, or is silenced. Either way the later entry overwrites the earlier one in the store.

Source

Thrown at packages/astro/src/content/loaders/file.ts:99

				const id = (rawItem.id ?? rawItem.slug)?.toString();
				if (!id) {
					logger.error(`Item in ${fileName} is missing an id or slug field.`);
					continue;
				}
				if (idList.has(id)) {
					const message = DuplicateContentEntrySlugError.message(
						collection,
						id,
						fileName,
						fileName,
					);
					if (config.prerenderConflictBehavior === 'error') {
						throw new AstroError({
							...DuplicateContentEntrySlugError,
							message,
						});
					} else if (config.prerenderConflictBehavior !== 'ignore') {
						logger.warn(message);
					}
				}
				idList.add(id);
				const parsedData = await parseData({ id, data: rawItem, filePath });
				store.set({ id, data: parsedData, filePath: normalizedFilePath });
			}
		} else if (typeof data === 'object') {
			const entries = Object.entries<Record<string, unknown>>(data);
			logger.debug(`Found object with ${entries.length} entries in ${fileName}`);
			store.clear();
			for (const [id, rawItem] of entries) {
				if (id === '$schema' && typeof rawItem === 'string') {
					// Ignore JSON schema field.
					continue;
				}
				const parsedData = await parseData({ id, data: rawItem, filePath });
				store.set({ id, data: parsedData, filePath: normalizedFilePath });
			}

View on GitHub (pinned to 52e6c34790)

Solutions

  1. Make every entry's id (or slug) unique in the data file
  2. Set prerenderConflictBehavior: 'error' in astro.config.mjs during development so collisions fail the build early instead of silently overwriting
  3. If collisions come from upstream data, preprocess the file to dedupe before Astro reads it

Example fix

// before: src/data/tags.json
[
  { "id": "news", "label": "News" },
  { "id": "news", "label": "News (old)" }
]

// after
[
  { "id": "news", "label": "News" },
  { "id": "news-archive", "label": "News (old)" }
]
Defensive patterns

Strategy: validation

Validate before calling

// Detect duplicate ids/slug collisions before Astro loads the file
import { readFileSync } from 'node:fs';
function findDuplicateIds(path: string): string[] {
  const items: Array<Record<string, unknown>> = JSON.parse(readFileSync(path, 'utf-8'));
  const ids = items.map((i) => String(i.id ?? i.slug));
  return [...new Set(ids.filter((id, i) => ids.indexOf(id) !== i))];
}

Try / catch

// With prerenderConflictBehavior: 'error', duplicate ids throw during content sync:
import { AstroError } from 'astro';
try {
  await runContentSync(); // astro sync / build, e.g. via the Astro JS API
} catch (err) {
  if (err instanceof AstroError && err.name === 'DuplicateContentEntrySlugError') {
    console.error('Fix duplicate ids listed in:', err.message);
    process.exitCode = 1;
  } else throw err;
}

Prevention

When it happens

Trigger: Two objects in the array sharing the same id (or slug) value; ids are coerced with toString(), so numeric 1 and string "1" also collide.

Common situations: Copy-pasted entries in JSON/YAML data; ids derived from titles that collide after normalization; merging data files from multiple sources without deduplication.

Related errors


AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18). Data as JSON: /api/errors/9eba6ab64e5fdb70. Report an issue: GitHub.