{"record":{"id":"d212e8f990d00b93","repo":"withastro/astro","slug":"error-when-reading-content-directory-contentdir","errorCode":null,"errorMessage":"Error when reading content directory \"${contentDir}\"","messagePattern":"Error when reading content directory \"(.+?)\"","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/astro/src/content/utils.ts","lineNumber":298,"sourceCode":"\ttry {\n\t\tif (!fs.existsSync(contentDirPath) || !fs.lstatSync(contentDirPath).isDirectory()) {\n\t\t\treturn contentPaths;\n\t\t}\n\t} catch {\n\t\t// Ignore if there isn't a valid content directory\n\t\treturn contentPaths;\n\t}\n\ttry {\n\t\tconst contentDirEntries = await fs.promises.readdir(contentDir, { withFileTypes: true });\n\t\tfor (const entry of contentDirEntries) {\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\tconst entryPath = path.join(contentDirPath, entry.name);\n\t\t\t\tconst realPath = await fs.promises.realpath(entryPath);\n\t\t\t\tcontentPaths.set(normalizePath(realPath), entry.name);\n\t\t\t}\n\t\t}\n\t} catch (e) {\n\t\tlogger.warn('content', `Error when reading content directory \"${contentDir}\"`);\n\t\tlogger.debug('content', e);\n\t\t// If there's an error, return an empty map\n\t\treturn new Map<string, string>();\n\t}\n\n\treturn contentPaths;\n}\n\nexport function reverseSymlink({\n\tentry,\n\tsymlinks,\n\tcontentDir,\n}: {\n\tentry: string | URL;\n\tcontentDir: string | URL;\n\tsymlinks?: Map<string, string>;\n}): string {\n\tconst entryPath = normalizePath(typeof entry === 'string' ? entry : fileURLToPath(entry));","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/withastro/astro/blob/e294953aa8aadd98d5be92e60a03037b05dbdfd4/packages/astro/src/content/utils.ts#L280-L316","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before: dangling symlink under src/content\nfind src/content -xtype l\n# src/content/posts -> ../../shared/posts (target moved)\n\n# after: relink to an existing, readable target\nln -sfn ../../packages/shared/posts src/content/posts","handlingStrategy":"validation","validationCode":"// run before `astro dev` / in CI — fails on dangling symlinks under src/content\nimport { readdirSync, lstatSync, readlinkSync, existsSync } from 'node:fs';\nimport path from 'node:path';\n\nfor (const name of readdirSync('src/content')) {\n  const p = path.join('src/content', name);\n  if (lstatSync(p).isSymbolicLink()) {\n    const target = path.resolve(path.dirname(p), readlinkSync(p));\n    if (!existsSync(target)) throw new Error(`Dangling content symlink: ${p} -> ${target}`);\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["content-collections","symlinks","filesystem"],"backgroundTag":"dangling-symlink-read-failure","analyzedSha":"e294953aa8aadd98d5be92e60a03037b05dbdfd4","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}