withastro/astro · error · AstroError

UnknownContentCollectionError

UnknownContentCollectionError

Error message

Unexpected error reading entry ${fileId}.

What it means

Thrown when `fs.promises.readFile` fails while reading a content entry file inside `getEntryModuleBaseInfo` in the content imports Vite plugin. Unlike error 103, this path attaches the original stack trace rather than using `cause`. It wraps any filesystem read failure as `UnknownContentCollectionError` with a message containing the file ID.

Source

Thrown at packages/astro/src/content/vite-plugin-content-imports.ts:347

	};

	return dataEntryModule;
}

// Shared logic for `getContentEntryModule` and `getDataEntryModule`
// Extracting to a helper was easier that conditionals and generics :)
async function getEntryModuleBaseInfo<TEntryType extends ContentEntryType | DataEntryType>({
	fileId,
	entryConfigByExt,
	contentDir,
	fs,
}: GetEntryModuleParams<TEntryType>) {
	const contentConfig = await getContentConfigFromGlobal();
	let rawContents;
	try {
		rawContents = await fs.promises.readFile(fileId, 'utf-8');
	} catch (e) {
		throw new AstroError({
			...AstroErrorData.UnknownContentCollectionError,
			message: `Unexpected error reading entry ${JSON.stringify(fileId)}.`,
			stack: e instanceof Error ? e.stack : undefined,
		});
	}
	const fileExt = extname(fileId);
	const entryConfig = entryConfigByExt.get(fileExt);

	if (!entryConfig) {
		throw new AstroError({
			...AstroErrorData.UnknownContentCollectionError,
			message: `No parser found for data entry ${JSON.stringify(
				fileId,
			)}. Did you apply an integration for this file type?`,
		});
	}
	const entry = pathToFileURL(fileId);
	const collection = getEntryCollectionName({ entry, contentDir });

View on GitHub (pinned to d081033d5f)

Solutions

  1. Check that the file ID in the JSON-stringified error message exists on disk.
  2. Restart the dev server to clear stale module graph entries.
  3. Fix filesystem permissions on the affected file.
  4. Ensure no external process (linter, formatter, sync tool) is deleting or locking content files during build.
Defensive patterns

Strategy: try-catch

Validate before calling

import { existsSync } from 'fs';
function preCheckEntry(fileId: string) {
  if (!existsSync(fileId)) {
    throw new Error(`Content entry missing before build: ${fileId}`);
  }
}

Try / catch

try {
  await build();
} catch (e) {
  if (e instanceof Error && e.message.includes('Unexpected error reading entry')) {
    console.error('Stale Vite cache or deleted file:', e.message);
  }
  throw e;
}

Prevention

When it happens

Trigger: The content imports plugin's module resolution calls `getEntryModuleBaseInfo` for an entry ID. The `readFile` for that ID throws (ENOENT, EACCES, etc.), and the error is re-thrown as an AstroError with the original error's stack.

Common situations: HMR triggered for a file that was just deleted. File watcher fires for a temp file created by an editor. Permission denied on a content file. A virtual or stale module ID is passed after the source was renamed.

Related errors


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