withastro/astro · error · AstroUserError
A content collection is defined with legacy features (e.g…
Error message
A content collection is defined with legacy features (e.g. missing a `loader` or has a `type`). Check your collection definitions in ${importerFilename ?? 'your content config file'} to ensure that all collections are defined using the current properties. What it means
In the content-layer API, a collection with a `loader` is a content-layer collection and must not carry a legacy `type`. `defineCollection` throws this AstroUserError when `config.type` is present and is anything other than `'content_layer'`, catching half-migrated configs that combine the legacy (`type: 'content' | 'data'`) and loader-based APIs.
Solutions
- Delete the `type` property — it is inferred (`content_layer`) whenever a loader is present
- Audit the collection for other legacy keys per the content-layer migration guide and remove them
- Follow the Astro v4 -> v5 content collections migration guide to update all collections at once
Example fix
// before
export const collections = {
posts: defineCollection({
type: 'content',
loader: glob({ pattern: '**/*.md', base: './src/content/posts' }),
}),
};
// after
export const collections = {
posts: defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/posts' }),
}),
}; Defensive patterns
Strategy: validation
Validate before calling
// normalize a migrated collection before defineCollection
function modernizeCollection(config) {
if ('loader' in config) {
delete config.type; // inferred as 'content_layer' when a loader is present
}
return config;
}
export const collections = {
posts: defineCollection(modernizeCollection(cfg)),
}; Prevention
- When adding a `loader` to an old collection, delete `type` in the same edit
- Follow the official Astro v5 content-layer migration guide end to end rather than patching piecemeal
- Grep content.config.ts for `type:` after any Astro major upgrade to catch leftovers
When it happens
Trigger: `defineCollection({ type: 'content', loader: glob({...}) })` or `{ type: 'data', loader: file(...) }` in src/content.config.ts — an old Astro 2/3-style definition that gained a loader without dropping `type`.
Common situations: Upgrading a project from Astro <=4 to Astro 5, where `type` became optional/inferred and loaders became required; following an old tutorial that includes `type` and adding a loader on top; generated config templates that mix eras.
Related errors
- Collection loader for
- ContentLoaderInvalidDataError
- ContentLoaderReturnsInvalidId
- ExpectedImageOptions
- Live content collections must be defined in…
AI-assisted analysis of withastro/astro@157c500c38 (2026-08-18).
Data as JSON: /api/errors/25f17f2ab6db4e4f.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/content/config.ts:195
export function defineCollection<
S extends BaseSchema,
TLoader extends LoaderConstraint<{ id: string }> = LoaderConstraint<{ id: string }>,
>(config: CollectionConfig<S, TLoader>): CollectionConfig<S, TLoader> {
const importerFilename = getImporterFilename();
if (importerFilename?.includes('live.config')) {
throw new AstroError({
...AstroErrorData.LiveContentConfigError,
message: AstroErrorData.LiveContentConfigError.message(
'Collections in a live config file must use `defineLiveCollection`.',
importerFilename,
),
});
}
if ('loader' in config) {
if (config.type && config.type !== CONTENT_LAYER_TYPE) {
throw new AstroUserError(
`A content collection is defined with legacy features (e.g. missing a \`loader\` or has a \`type\`). Check your collection definitions in ${importerFilename ?? 'your content config file'} to ensure that all collections are defined using the current properties.`,
);
}
if (
typeof config.loader === 'object' &&
typeof config.loader.load !== 'function' &&
('loadEntry' in config.loader || 'loadCollection' in config.loader)
) {
throw new AstroUserError(
`Live content collections must be defined in "src/live.config.ts" file. Check the loaders used in "${importerFilename ?? 'your content config file'}" to ensure you are not using a live loader to define a build-time content collection.`,
);
}
config.type = CONTENT_LAYER_TYPE;
}
if (!config.type) config.type = 'content';
return config;
}
View on GitHub (pinned to 157c500c38)