withastro/astro · error · AstroError
LegacyContentConfigError
LegacyContentConfigError
Error message
Found legacy content config file in "${relativePath}". Please move this file to "src/content.config.${filename.split('.').at(-1)}" and ensure each collection has a loader defined. What it means
Astro 5 moved the content config from src/content/config.ts to src/content.config.ts and requires every collection to declare a loader. When getContentPaths finds only the legacy file and the legacy.collectionsBackwardsCompat flag is not enabled, it throws this error instructing you to migrate.
Solutions
- Move src/content/config.ts to src/content.config.ts (in src/, not inside src/content/) and give each collection a loader such as glob().
- As a temporary bridge, set legacy: { collectionsBackwardsCompat: true } in astro.config.mjs.
- Delete the legacy file after migrating so only the new config is found.
Example fix
// before: src/content/config.ts (Astro <=4)
import { defineCollection } from 'astro:content';
const blog = defineCollection({ schema: z.object({ title: z.string() }) });
export const collections = { blog };
// after: src/content.config.ts
import { defineCollection, glob } from 'astro/loaders';
const blog = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
schema: z.object({ title: z.string() }),
});
export const collections = { blog }; Defensive patterns
Strategy: validation
Validate before calling
// Migration guard: fail with a clear message if only the legacy config exists
import { existsSync } from 'node:fs';
const modern = 'src/content.config.ts';
const legacy = 'src/content/config.ts';
if (!existsSync(modern) && existsSync(legacy)) {
throw new Error('Run the Astro 5 migration: move src/content/config.ts to src/content.config.ts');
} Prevention
- After upgrading Astro majors, run astro upgrade and read the migration guide for content collections.
- Ensure every migrated collection declares a loader (glob(), file(), or custom).
- Delete the legacy file once migrated so both configs never compete.
- Use legacy.collectionsBackwardsCompat only as a temporary bridge.
When it happens
Trigger: Upgrading a project to Astro 5+ while src/content/config.ts (or .mjs) still exists; scaffolding a new project by copying an Astro 4-era layout; the modern config file missing so the legacy one is detected.
Common situations: astro upgrade from v3/v4; CI failing after a major bump; following tutorials or templates predating Astro 5.
Related errors
- Astro.locals.runtime.caches has been removed in Astro v6…
- Astro.locals.runtime.cf has been removed in Astro v6. Use…
- Astro.locals.runtime.ctx has been removed in Astro v6. Use…
- Astro.locals.runtime.env has been removed in Astro v6. Use…
- LiveContentConfigError
AI-assisted analysis of withastro/astro@e294953aa8 (2026-08-18).
Data as JSON: /api/errors/48dc63f799d60531.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/content/utils.ts:737
};
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 e294953aa8)