immich-app/immich · critical · ImmichStartupError

Failed to create

Error message

Failed to create "${externalPath} - ${docsMessage}"

What it means

During bootstrap, Immich creates a mount-check file per storage folder to validate write access; if creation fails with an error other than EEXIST, it raises an ImmichStartupError naming the external path, because Immich cannot write to the configured storage location.

Solutions

  1. Fix ownership/permissions so the Immich user can write: chown -R <uid>:<gid> on the data directory (commonly 1000:1000).
  2. Remove ':ro' from the volume mount if present and remount the volume read-write.
  3. Pre-create the host folder with correct ownership so Docker doesn't create it as root.
  4. Check disk space / mount health (df -h, mount status) if permissions look correct.

Example fix

// before (docker-compose.yml)
volumes:
  - ./upload:/usr/src/app/upload:ro
// after
volumes:
  - ./upload:/usr/src/app/upload
// plus: chown -R 1000:1000 ./upload
Defensive patterns

Strategy: try-catch

Validate before calling

// before starting, verify each storage folder is writable by the immich user
import { accessSync, constants } from 'node:fs';
for (const dir of storageFolders) accessSync(dir, constants.W_OK);

Try / catch

try {
  await startImmichServer();
} catch (e) {
  if (/Failed to create \"/.test(String(e.message))) {
    // fix ownership/permissions or read-only mount, then restart
    process.exit(1);
  } else throw e;
}

Prevention

When it happens

Trigger: Starting Immich when the storage folder is read-only, the volume is mounted but the process user lacks write permission, or the folder doesn't exist and cannot be created.

Common situations: Bind mounts owned by root while Immich runs as another UID; read-only volume mounts (':ro'); disk-full or NFS with root-squash; missing host directory in docker-compose so Docker creates a root-owned folder.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15). Data as JSON: /api/errors/c86aae6c7152ff4f. Report an issue: GitHub.

Appendix: 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 e55ac299a4)