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
- Move `src/content/config.ts` to `src/content.config.ts` (keeping the same extension).
- Add a `loader` to every collection definition (e.g. `glob({ pattern: '**/*.md', base: './src/content/blog' })`).
- Remove the `type` field from collection definitions if present.
- 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
- After upgrading to Astro 6, immediately move src/content/config.* to src/content.config.*.
- Add loaders to every collection during migration.
- Run the Astro upgrade guide checklist before deploying.
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
- A content collection is defined with legacy features (e.g. m
- LiveContentConfigError
- LiveContentConfigError
- LiveContentConfigError
- CONTENT_TOO_LARGE
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/48dc63f799d60531.
Report an issue: GitHub.