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

  1. Remove the `type` field from the collection config — it will default to `content` automatically.
  2. Ensure you pass a `loader` (e.g. `glob()`, `file()`) for content-layer collections.
  3. 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

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


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