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 distinct files ingested by the glob() loader generated the same entry id (from id/slug frontmatter or filename slugification), so the second entry overwrites the first in the store. The astro.config option prerenderConflictBehavior (default 'warn') decides whether this throws an AstroError with the DuplicateContentEntrySlugError message, logs a warning, or is ignored.

Source

Thrown at packages/astro/src/content/loaders/glob.ts:203

				if (existingEntry && existingEntry.filePath && existingEntry.filePath !== relativePath) {
					// Check the old file still exists - if not, this is likely a rename and
					// the unlink event just hasn't been processed yet
					const oldFilePath = new URL(existingEntry.filePath, config.root);
					if (existsSync(oldFilePath)) {
						const message = AstroErrorData.DuplicateContentEntrySlugError.message(
							collection,
							id,
							existingEntry.filePath,
							relativePath,
						);
						if (config.prerenderConflictBehavior === 'error') {
							throw new AstroError({
								...AstroErrorData.DuplicateContentEntrySlugError,
								message,
							});
						} else if (config.prerenderConflictBehavior !== 'ignore') {
							logger.warn(message);
						}
					}
				}

				if (entryType.getRenderFunction && !globOptions.deferRender) {
					let render = renderFunctionByContentType.get(entryType);

					if (!render) {
						render = await entryType.getRenderFunction(config);
						// Cache the render function for this content type, so it can re-use parsers and other expensive setup
						renderFunctionByContentType.set(entryType, render);
					}
					let rendered: RenderedContent | undefined = undefined;

					try {
						rendered = await render?.({
							id,
							data,

View on GitHub (pinned to e294953aa8)

Solutions

  1. Uniquify the frontmatter slug or the filename of the colliding entry
  2. Set prerenderConflictBehavior: 'error' in astro.config.mjs to surface collisions as build failures
  3. Restart the dev server so stale ids from renamed files are cleaned from the store

Example fix

# before: src/data/blog/bar.md has frontmatter slug: bar, and src/data/blog/foo.md also sets slug: bar
# after: src/data/blog/foo.md
---
title: Foo
slug: foo
---
Defensive patterns

Strategy: validation

Validate before calling

// Scan frontmatter slugs + filenames for collisions before build
import { readFileSync, readdirSync } from 'node:fs';
import matter from 'gray-matter';
function duplicateSlugs(dir: string): string[] {
  const slugs = readdirSync(dir)
    .filter((f) => f.endsWith('.md'))
    .map((f) => (matter(readFileSync(`${dir}/${f}`, 'utf-8')).data as any).slug ?? f.replace(/\.md$/, ''));
  return [...new Set(slugs.filter((s, i) => slugs.indexOf(s) !== i))];
}

Try / catch

// With prerenderConflictBehavior: 'error', slug collisions throw during build:
import { AstroError } from 'astro';
try {
  await runBuild();
} catch (err) {
  if (err instanceof AstroError && err.name === 'DuplicateContentEntrySlugError') {
    // the message lists the collection and both colliding file paths
    console.error(err.message);
    process.exitCode = 1;
  } else throw err;
}

Prevention

When it happens

Trigger: foo.md carrying `slug: bar` in frontmatter alongside an existing bar.md; two files in different matched directories whose generated slugs collide; stale ids left in the store after renames (the loader separately handles oldId deletion on watch events).

Common situations: Explicit frontmatter slugs that match other filenames; case-insensitive filesystems colliding with case-sensitive slugs; content merges introducing duplicate slugs.

Related errors


AI-assisted analysis of withastro/astro@e294953aa8 (2026-08-18). Data as JSON: /api/errors/6090a8ac4f9e3a4d. Report an issue: GitHub.