immich-app/immich · critical · ImmichStartupError
Failed to create "${externalPath} - ${docsMessage}"
Error message
Failed to create "${externalPath} - ${docsMessage}" What it means
Thrown by StorageService.createMountFile at startup when mkdir of the folder path or creation of the '.immich' marker file fails with an errno other than EEXIST (EEXIST is tolerated). It aborts boot via ImmichStartupError, including the external path and the folder-checks doc link.
Source
Thrown at server/src/services/storage.service.ts:176
await this.storageRepository.readFile(internalPath);
} catch (error) {
this.logger.error(`Failed to read (${internalPath}): ${error}`);
throw new ImmichStartupError(`Failed to read: "${externalPath} (${internalPath}) - ${docsMessage}"`);
}
}
private async createMountFile(folder: StorageFolder) {
const { folderPath, internalPath, externalPath } = this.getMountFilePaths(folder);
try {
this.storageRepository.mkdirSync(folderPath);
await this.storageRepository.createFile(internalPath, Buffer.from(Date.now().toString()));
} catch (error) {
if ((error as NodeJS.ErrnoException).code === 'EEXIST') {
this.logger.warn('Found existing mount file, skipping creation');
return;
}
this.logger.error(`Failed to create ${internalPath}: ${error}`);
throw new ImmichStartupError(`Failed to create "${externalPath} - ${docsMessage}"`);
}
}
private async verifyWriteAccess(folder: StorageFolder) {
const { internalPath, externalPath } = this.getMountFilePaths(folder);
try {
await this.storageRepository.overwriteFile(internalPath, Buffer.from(Date.now().toString()));
} catch (error) {
this.logger.error(`Failed to write ${internalPath}: ${error}`);
throw new ImmichStartupError(`Failed to write "${externalPath} - ${docsMessage}"`);
}
}
private getMountFilePaths(folder: StorageFolder) {
const folderPath = StorageCore.getBaseFolder(folder);
const internalPath = join(folderPath, '.immich');
const externalPath = `<UPLOAD_LOCATION>/${folder}/.immich`;
View on GitHub (pinned to 199723261c)
Solutions
- Free space on the target volume and retry boot.
- Confirm the bind mount is not ':ro' and the immich uid has write permission to the folder.
- Create the parent directory on the host if it is missing, then restart.
- On SELinux-enabled hosts, relabel the mount: chcon -Rt container_file_t <host path>.
- Inspect the prior log line 'Failed to create <internalPath>: <error>' for the exact errno.
Example fix
chmod -R u+rwX /mnt/immich/upload && chown -R 1000:1000 /mnt/immich/upload
Defensive patterns
Strategy: validation
Validate before calling
import { accessSync, constants, mkdirSync } from 'node:fs';
function ensureWritableDir(p: string) {
mkdirSync(p, { recursive: true });
accessSync(p, constants.W_OK | constants.X_OK);
}
// call ensureWritableDir(folderPath) for each StorageFolder before boot Try / catch
try { await storageService.onBootstrap(); }
catch (e) {
if (e instanceof ImmichStartupError && /Failed to create/.test(e.message)) {
// check disk space & permissions, then restart - do not loop
}
} Prevention
- Alert on free space < 5% on volumes backing upload/library/encoded folders.
- Avoid ':ro' bind mounts for any Immich-managed folder.
- On SELinux hosts, label host paths with container_file_t before first boot.
When it happens
Trigger: Server boot, folder-integrity phase: storageRepository.mkdirSync(folderPath) or createFile(internalPath, ...) throws a non-EEXIST error (e.g. EACCES, ENOSPC, EROFS).
Common situations: Disk full on the volume backing the upload/library folder; read-only filesystem (read-only bind mount or read-only rootfs); SELinux/AppCore denying write; parent directory missing because a prior mount step failed.
Related errors
- Failed to read: "${externalPath} (${internalPath}) - ${docsM
- Failed to write "${externalPath} - ${docsMessage}"
- Failed to read helmet file: ${helmetFile}
- Geodata file ${cities500} not found
- Target media location must be an absolute path
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/c86aae6c7152ff4f.
Report an issue: GitHub.