immich-app/immich · warning

Failed to read

Error message

Failed to read ${resourcePaths.lockFile}

What it means

getBuildVersions reads a build lockfile (JSON) to report bundled tool versions (node, ffmpeg, imagemagick, exiftool). If the lockfile cannot be read or parsed, the read promise rejects and this warning is logged; version fields fall back to runtime commands or undefined. It is non-fatal — build/version metadata is simply incomplete.

Solutions

  1. Verify the lockfile exists at the path shown in the warning (check resourcePaths / storage layout).
  2. If running from source, this is expected — rely on the fallback version retrieval commands.
  3. Reinstall/re-pull the official Docker image so the lockfile ships with the deployment.
  4. Fix filesystem permissions on the resources directory if the file exists but is unreadable.

Example fix

// before
.catch(() => this.logger.warn(`Failed to read ${resourcePaths.lockFile}`));
// after
.catch((error: unknown) => {
  this.logger.warn(`Failed to read ${resourcePaths.lockFile}: ${error}`);
  return undefined;
});
Defensive patterns

Strategy: fallback

Validate before calling

import { existsSync } from 'node:fs';
const hasLockfile = existsSync(resourcePaths.lockFile);

Type guard

const isBuildLockfile = (v: unknown): v is BuildLockfile =>
  typeof v === 'object' && v !== null && 'ffmpegVersion' in v;

Try / catch

let lockfile: BuildLockfile | undefined;
try {
  lockfile = JSON.parse(await readFile(path, 'utf8'));
} catch (e) {
  logger.warn(`No build lockfile at ${path}; using runtime version fallback`, e);
  lockfile = undefined;
}

Prevention

When it happens

Trigger: readFile(resourcePaths.lockFile) rejects (file missing/unreadable) or JSON.parse throws, inside getBuildVersions when this.buildVersions is not yet cached.

Common situations: Running from source without the packaged build-lockfile; Docker image built without the lock file; custom deployment layout where resourcePaths differ; permission problem on the resources directory.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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

Appendix: source

Thrown at server/src/repositories/server-info.repository.ts:105

    command: string,
    commandTransform?: (output: string) => string,
    version?: string,
  ): Promise<string> {
    if (!version) {
      const output = await maybeFirstLine(command);
      version = commandTransform ? commandTransform(output) : output;
    }
    return version;
  }

  async getBuildVersions(): Promise<ServerBuildVersions> {
    if (!this.buildVersions) {
      const { nodeVersion, resourcePaths } = this.configRepository.getEnv();

      const lockfile: BuildLockfile | undefined = await readFile(resourcePaths.lockFile)
        .then((buffer) => JSON.parse(buffer.toString()))

        .catch(() => this.logger.warn(`Failed to read ${resourcePaths.lockFile}`));

      const [nodejsVersion, ffmpegVersion, magickVersion, exiftoolVersion] = await Promise.all([
        this.retrieveVersionFallback('node --version', undefined, nodeVersion),
        this.retrieveVersionFallback(
          'ffmpeg -version',
          (output) => output.replaceAll('ffmpeg version ', ''),
          getLockfileVersion('ffmpeg', lockfile),
        ),
        this.retrieveVersionFallback(
          'magick --version',
          (output) => output.replaceAll('Version: ImageMagick ', ''),
          getLockfileVersion('imagemagick', lockfile),
        ),
        exiftool.version(),
      ]);

      // eslint-disable-next-line import-x/no-named-as-default-member
      const libvipsVersion = getLockfileVersion('libvips', lockfile) || sharp.versions.vips;

View on GitHub (pinned to e55ac299a4)