withastro/astro · error · AstroError

DuplicateContentEntrySlugError

DuplicateContentEntrySlugError

Error message

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

Entries: 
- ${fileName}
- ${fileName}

What it means

In the array form, the file() loader derives each entry's id from its `id` or `slug` field and requires uniqueness. When two items resolve to the same id within one file, Astro reports DuplicateContentEntrySlugError — a warning by default, a thrown error when `prerenderConflictBehavior` is set to 'error'. The message lists the same file twice because both colliding entries live in that one file.

Source

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

			}
			logger.debug(`Found ${data.length} item array in ${fileName}`);
			store.clear();
			const idList = new Set();
			for (const rawItem of data) {
				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.

View on GitHub (pinned to 52e6c34790)

Solutions

  1. Find the duplicated id/slug printed in the message and make it unique in the data file.
  2. If ids come from user content, normalize them (slugify) and append a suffix on collision before writing the file.
  3. Only if duplicates are acceptable, adjust `prerenderConflictBehavior` ('warn' logs, 'ignore' silences) — but the store will keep the last entry for a duplicate id.

Example fix

// before (data.json)
[
  { "id": "getting-started", "title": "Getting Started" },
  { "id": "getting-started", "title": "Getting Started (old)" }
]

// after
[
  { "id": "getting-started", "title": "Getting Started" },
  { "id": "getting-started-v1", "title": "Getting Started (old)" }
]
Defensive patterns

Strategy: validation

Validate before calling

function hasUniqueIds(items: Array<Record<string, any>>): boolean {
  const ids = items.map((i) => (i.id ?? i.slug)?.toString()).filter(Boolean);
  return new Set(ids).size === ids.length;
}

if (!hasUniqueIds(data)) {
  const seen = new Set<string>();
  const dup = data.find((i) => {
    const id = (i.id ?? i.slug)?.toString();
    if (seen.has(id)) return true;
    seen.add(id);
    return false;
  });
  throw new Error(`Duplicate id: ${dup?.id ?? dup?.slug}`);
}

Try / catch

try {
  await getCollection('authors');
} catch (err) {
  if ((err as any)?.code === 'DuplicateContentEntrySlugError') {
    // the message names the colliding slug; dedupe it in the data file and re-sync
  } else throw err;
}

Prevention

When it happens

Trigger: Two objects in the loaded array with the same `id` (or `slug`) value; ids that differ in the source but stringify identically (e.g. numeric 1 and string '1' both become '1' via toString()).

Common situations: Copy-pasted entries in a hand-maintained JSON/YAML data file; YAML numeric ids colliding with string ids after coercion; large datasets where duplicate slugs slip in unnoticed during editing.

Related errors


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