withastro/astro · error · AstroError
UnknownFilesystemError
UnknownFilesystemError
Error message
An unknown error occurred while reading or writing files to disk.
What it means
The mutable data store persists generated asset-import modules (e.g. .astro/assets-imports.mjs) using an atomic temp-file-plus-rename write. When the store has zero asset imports and writing the empty `export default new Map();` stub fails, the raw filesystem error is wrapped in AstroError code UnknownFilesystemError with the original error attached as `cause`.
Solutions
- Inspect `error.cause.code` (EACCES, EPERM, ENOSPC, EBUSY) — the real reason is never in the AstroError message itself.
- Fix ownership and clear the generated dir: `sudo chown -R $(whoami) .astro` then `rm -rf .astro` and rebuild.
- In containers/CI, ensure the user can write the project directory (volume permissions, `user` in docker-compose).
- Free disk space or exclude the project folder from antivirus/sync tools if rename locks persist.
Example fix
# before: .astro left root-owned or locked, build fails with UnknownFilesystemError sudo chown -R "$(whoami)" . .astro rm -rf .astro pnpm astro build
Defensive patterns
Strategy: try-catch
Validate before calling
import { access, constants } from 'node:fs/promises';
import { mkdtemp } from 'node:fs/promises';
async function canWriteProjectDir(root: string): Promise<boolean> {
const probe = await mkdtemp(join(root, '.write-probe-'));
await rm(probe, { recursive: true });
return true;
} Type guard
import { isAstroError } from 'astro/errors';
const isFilesystemError = (err: unknown): err is { code: string } => {
const cause = (err as any)?.cause ?? err;
return typeof cause?.code === 'string';
}; Try / catch
try {
await runAstroBuild(); // programmatic build/sync that writes .astro
} catch (err) {
if (isAstroError(err) && (err as any).code === 'UnknownFilesystemError') {
const cause = (err as any).cause;
if (['EACCES', 'EPERM'].includes(cause?.code)) {
// fix ownership of .astro / project dir, then retry once
} else if (cause?.code === 'ENOSPC') {
// free disk space, then retry
} else if (cause?.code === 'EBUSY') {
// another process holds the file: stop duplicate dev servers / AV scan, retry after backoff
} else {
throw err;
}
} else throw err;
} Prevention
- Never run astro under sudo; if you did, chown .astro back to your user.
- In CI/Docker, run as a user with write access to the project directory.
- Exclude the project (at least .astro) from antivirus and sync clients.
- Run one Astro process per checkout.
When it happens
Trigger: Any OS-level failure writing into the .astro directory: EACCES/EPERM (wrong ownership or read-only dir), ENOSPC (full disk), EBUSY/EPERM on rename (file locked by antivirus or a sync client), or a read-only filesystem in a container.
Common situations: Running `astro dev`/`astro build` once under sudo so .astro is root-owned; Docker/CI containers running as a non-root user without write access to the project dir; OneDrive/Dropbox locking generated files; full disks during large builds.
Understand the failure class
Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.
Related errors
- Failed to write lock file
- UnknownContentCollectionError
- Content config not loaded
- Error when reading content directory
- No contents found for
AI-assisted analysis of withastro/astro@e294953aa8 (2026-08-18).
Data as JSON: /api/errors/9067ea2d9c9917f5.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/content/mutable-data-store.ts:188
if (typedEntry.deferredRender && typedEntry.filePath) {
const id = contentModuleToId(typedEntry.filePath);
if (id) {
this.#moduleImports.set(typedEntry.filePath, id);
}
}
}
}
}
async writeAssetImports(filePath: PathLike) {
this.#assetsFile = filePath;
this.#rebuildAssetImports();
if (this.#assetImports.size === 0) {
try {
await this.#writeFileAtomic(filePath, 'export default new Map();');
} catch (err) {
throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause: err });
}
}
if (!this.#assetsDirty && existsSync(filePath)) {
return;
}
// Import the assets, with a symbol name that is unique to the import id. The import
// for each asset is an object with path, format and dimensions.
// We then export them all, mapped by the import id, so we can find them again in the build.
const imports: Array<string> = [];
const exports: Array<string> = [];
// Sort asset imports to ensure deterministic output across builds
const sortedAssetImports = [...this.#assetImports].sort();
sortedAssetImports.forEach((id, index) => {
const symbol = `__ASTRO_IMAGE_IMPORT_${index}`;
imports.push(`import ${symbol} from ${JSON.stringify(id)};`);
exports.push(`[${JSON.stringify(id)}, ${symbol}]`);
});View on GitHub (pinned to e294953aa8)