immich-app/immich · error · BadRequestException

Unable to process profile image

Error message

Unable to process profile image

What it means

A BadRequestException (HTTP 400) thrown by UserService.createProfileImage wrapping the underlying cause when generateProfileImage fails. Before throwing, the service queues deletion of the uploaded temp file, so the uploaded artifact is cleaned up. The original error is attached via { cause } for diagnostics.

Source

Thrown at server/src/services/user.service.ts:118

    const user = await this.findOrFail(id, { withDeleted: false });
    return mapUser(user);
  }

  async createProfileImage(auth: AuthDto, file: Express.Multer.File): Promise<CreateProfileImageResponseDto> {
    const { profileImagePath: oldPath } = await this.findOrFail(auth.user.id, { withDeleted: false });

    let profileImagePath: string;
    try {
      const config = await this.getConfig({ withCache: true });
      profileImagePath = await generateProfileImage(
        { media: this.mediaRepository, crypto: this.cryptoRepository, storageCore: this.storageCore },
        config,
        auth.user.id,
        file.path,
      );
    } catch (error) {
      await this.jobRepository.queue({ name: JobName.FileDelete, data: { files: [file.path] } });
      throw new BadRequestException('Unable to process profile image', { cause: error });
    }

    const user = await this.userRepository.update(auth.user.id, {
      profileImagePath,
      profileChangedAt: new Date(),
    });

    const toDelete = [file.path, ...(oldPath ? [oldPath] : [])];
    await this.jobRepository.queue({ name: JobName.FileDelete, data: { files: toDelete } });

    return {
      userId: user.id,
      profileImagePath: user.profileImagePath,
      profileChangedAt: user.profileChangedAt,
    };
  }

  async deleteProfileImage(auth: AuthDto): Promise<void> {

View on GitHub (pinned to 199723261c)

Solutions

  1. Inspect the cause field of the error response for the underlying sharp/storage error.
  2. Re-encode the image to JPEG or PNG before uploading and retry.
  3. Verify the server's sharp installation supports the source format (rebuild sharp or install libvips codecs).
  4. Check filesystem permissions and free space on the storage volume backing profile images.

Example fix

// before
await api.uploadProfileImage(file); // 400 'Unable to process profile image'

// after
const png = await convertToPng(file);
try {
  await api.uploadProfileImage(png);
} catch (e) {
  console.error(e.cause); // sharp error detail
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Convert/validate the file before upload
const SUPPORTED = ['image/jpeg', 'image/png', 'image/webp'];
if (!SUPPORTED.includes(file.type)) {
  return { ok: false, reason: 'Use JPEG, PNG, or WebP' };
}
if (file.size === 0) { return { ok: false, reason: 'File is empty' }; }

Type guard

const isProfileImageError = (e: unknown): boolean =>
  typeof e === 'object' && e !== null && (e as any).status === 400 && (e as any).message === 'Unable to process profile image';

Try / catch

try {
  return await api.uploadProfileImage(file);
} catch (e) {
  if (isProfileImageError(e)) {
    notify('Could not process that image. Try a JPEG or PNG.');
    console.error('cause:', (e as any).cause);
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: POST /users/profile-image with a file that sharp cannot decode (corrupt image, unsupported format, zero-byte file, or a non-image mime). Also possible when storage is unavailable or the media/crypto dependencies throw during resize.

Common situations: Uploading a HEIC/AVIF file the sharp build cannot decode; a truncated upload due to network drop; a misconfigured storage folder lacking write permission; sharp native bindings missing in the container.

Related errors


AI-assisted analysis of immich-app/immich@199723261c (2026-08-12). Data as JSON: /api/errors/068f9809783489e0. Report an issue: GitHub.