withastro/astro · error · AstroError

LegacyContentConfigError

LegacyContentConfigError

Error message

Found legacy content config file in "${filename}". Please move this file to "src/content.config.${ext}" and ensure each collection has a loader defined.

What it means

Thrown when Astro finds a legacy content config file (e.g. `src/content/config.ts`) but legacy collections backwards compatibility is not enabled. Astro 6 removed legacy content collections; config must now live at `src/content.config.*` with explicit loaders. The message tells you exactly where the legacy file is and where to move it.

Source

Thrown at packages/astro/src/content/utils.ts:729

};

export function getContentPaths(
	{ srcDir, root }: Pick<AstroConfig, 'root' | 'srcDir'>,
	fs: typeof fsMod = fsMod,
	legacyCollectionsBackwardsCompat = false,
): ContentPaths {
	const pkgBase = new URL('../../', import.meta.url);
	const configStats = searchConfig(fs, srcDir);

	if (!configStats.exists) {
		const legacyConfigStats = searchLegacyConfig(fs, srcDir);
		if (legacyConfigStats.exists) {
			if (!legacyCollectionsBackwardsCompat) {
				const relativePath = path.relative(
					fileURLToPath(root),
					fileURLToPath(legacyConfigStats.url),
				);
				throw new AstroError({
					...AstroErrorData.LegacyContentConfigError,
					message: AstroErrorData.LegacyContentConfigError.message(relativePath),
				});
			}
			// Use legacy config path when backwards compat is enabled
			return getContentPathsWithConfig(root, srcDir, pkgBase, legacyConfigStats, fs);
		}
	}

	const liveConfigStats = searchLiveConfig(fs, srcDir);
	return {
		root: new URL('./', root),
		contentDir: new URL('./content/', srcDir),
		assetsDir: new URL('./assets/', srcDir),
		typesTemplate: new URL('templates/content/types.d.ts', pkgBase),
		virtualModTemplate: new URL('templates/content/module.mjs', pkgBase),
		config: configStats,
		liveConfig: liveConfigStats,

View on GitHub (pinned to d081033d5f)

Solutions

  1. Move `src/content/config.ts` to `src/content.config.ts` (keeping the same extension).
  2. Add a `loader` to every collection definition (e.g. `glob({ pattern: '**/*.md', base: './src/content/blog' })`).
  3. Remove the `type` field from collection definitions if present.
  4. Optionally, temporarily enable backwards compat via `legacy: { collectionsBackwardsCompat: true }` in `astro.config` to ease migration, but plan to move off it.

Example fix

// before — src/content/config.ts
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({ type: 'content', schema: z.object({ title: z.string() }) });

// after — src/content.config.ts
import { defineCollection, z, glob } from 'astro:content';
const blog = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
  schema: z.object({ title: z.string() }),
});
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync } from 'fs';
if (existsSync('src/content/config.ts') || existsSync('src/content/config.js')) {
  console.error('Legacy config found — move to src/content.config.ts');
}

Prevention

When it happens

Trigger: `getContentPaths` is called, `searchConfig` finds no modern `src/content.config.*`, then `searchLegacyConfig` finds a legacy `src/content/config.*`. Since `legacyCollectionsBackwardsCompat` is false (the default in Astro 6), the error is thrown.

Common situations: Upgrading from Astro 5 to Astro 6 without migrating the content config location. Cloning an older project that uses the `src/content/config.ts` path. CI pipeline fails immediately after an Astro version bump.

Related errors


AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12). Data as JSON: /api/errors/48dc63f799d60531. Report an issue: GitHub.