withastro/astro · error · AstroUserError
A content collection is defined with legacy features (e.g. m
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} to ensure that all collections are defined using the current properties. What it means
`defineCollection` accepts a `loader` (content-layer style), but if the config also carries a legacy `type` field that is not the content-layer type, it is treated as a legacy (pre-content-layer) collection shape and rejected. Legacy collections used `type: 'content' | 'data'` and the implicit filesystem loader; the current API requires an explicit loader and no `type`.
Source
Thrown at packages/astro/src/content/config.ts:194
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 d081033d5f)
Solutions
- Remove the `type` field from the collection config — it will default to `content` automatically.
- Ensure you pass a `loader` (e.g. `glob()`, `file()`) for content-layer collections.
- Run `astro sync` after editing to confirm the config is accepted.
Example fix
// before
const posts = defineCollection({ type: 'content', schema });
// after
import { glob } from 'astro/loaders';
const posts = defineCollection({ loader: glob('src/content/posts/*.md'), schema }); Defensive patterns
Strategy: validation
Validate before calling
if ('loader' in config && config.type && config.type !== CONTENT_LAYER_TYPE) {
throw new Error('Remove the legacy type field; provide a loader instead');
} Prevention
- Drop `type` from `defineCollection` configs.
- Always pass an explicit `loader` for content-layer collections.
- Re-run `astro sync` after migrating legacy collection shapes.
When it happens
Trigger: Calling `defineCollection({ type: 'content', loader })` or `defineCollection({ type: 'data' })` in `src/content/config.ts`.
Common situations: Upgrading from an older Astro where `type: 'content'` was standard; copy-pasting legacy examples; an integration scaffold emitting the old shape.
Related errors
- LegacyContentConfigError
- LiveContentConfigError
- LiveContentConfigError
- LiveContentConfigError
- CONTENT_TOO_LARGE
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/31652b77273f8e03.
Report an issue: GitHub.