withastro/astro · warning
Error when reading content directory "${contentDir}"
Error message
Error when reading content directory "${contentDir}" What it means
During content-layer sync, Astro scans the content directory for symbolic links and resolves each one with fs.realpath so symlinked entries map to their real files. If readdir() or realpath() throws, Astro logs this warning (the underlying error only at debug level) and falls back to an empty symlink map, so sync continues but symlinked content entries are not tracked.
Source
Thrown at packages/astro/src/content/utils.ts:298
try {
if (!fs.existsSync(contentDirPath) || !fs.lstatSync(contentDirPath).isDirectory()) {
return contentPaths;
}
} catch {
// Ignore if there isn't a valid content directory
return contentPaths;
}
try {
const contentDirEntries = await fs.promises.readdir(contentDir, { withFileTypes: true });
for (const entry of contentDirEntries) {
if (entry.isSymbolicLink()) {
const entryPath = path.join(contentDirPath, entry.name);
const realPath = await fs.promises.realpath(entryPath);
contentPaths.set(normalizePath(realPath), entry.name);
}
}
} catch (e) {
logger.warn('content', `Error when reading content directory "${contentDir}"`);
logger.debug('content', e);
// If there's an error, return an empty map
return new Map<string, string>();
}
return contentPaths;
}
export function reverseSymlink({
entry,
symlinks,
contentDir,
}: {
entry: string | URL;
contentDir: string | URL;
symlinks?: Map<string, string>;
}): string {
const entryPath = normalizePath(typeof entry === 'string' ? entry : fileURLToPath(entry));View on GitHub (pinned to e294953aa8)
Solutions
- Find and repair dangling links: run `find src/content -xtype l` and remove or relink each match
- Verify the content directory exists and every symlink target is readable by the dev-server process
- Restart `astro dev` or re-run `astro sync` so the content layer re-scans
- If the warning persists, re-run with debug logging (logger.debug shows the caught error) to see the exact fs failure
Example fix
# before: dangling symlink under src/content find src/content -xtype l # src/content/posts -> ../../shared/posts (target moved) # after: relink to an existing, readable target ln -sfn ../../packages/shared/posts src/content/posts
Defensive patterns
Strategy: validation
Validate before calling
// run before `astro dev` / in CI — fails on dangling symlinks under src/content
import { readdirSync, lstatSync, readlinkSync, existsSync } from 'node:fs';
import path from 'node:path';
for (const name of readdirSync('src/content')) {
const p = path.join('src/content', name);
if (lstatSync(p).isSymbolicLink()) {
const target = path.resolve(path.dirname(p), readlinkSync(p));
if (!existsSync(target)) throw new Error(`Dangling content symlink: ${p} -> ${target}`);
}
} Prevention
- Keep content symlink targets inside the repository so they survive clones and CI
- Add the dangling-symlink check to CI before the build
- Avoid deleting/moving content directories while the dev server is syncing
When it happens
Trigger: fs.promises.readdir(contentDir) or fs.promises.realpath(entryPath) throws: the content directory was deleted or renamed while `astro dev` / `astro sync` was running, a dangling (broken) symlink exists under src/content (realpath returns ENOENT), or the process lacks read permission on the directory or a link target.
Common situations: Monorepos and npm/pnpm workspaces where src/content contains symlinks into a shared package; deleting or moving content files while the dev server is mid-sync; CI containers or read-only mounts where realpath fails on permission-restricted targets.
Related errors
- UnknownContentCollectionError
- UnknownContentCollectionError
- No contents found for ${entry}
- The base directory "${fileURLToPath(baseDir)}" does not exis
- UnknownFilesystemError
AI-assisted analysis of withastro/astro@e294953aa8 (2026-08-18).
Data as JSON: /api/errors/d212e8f990d00b93.
Report an issue: GitHub.