immich-app/immich · error · BadRequestException
Unable to process profile image
Error message
Unable to process profile image
What it means
createProfileImage fails when the uploaded profile image cannot be processed (resizing/conversion pipeline throws). The service queues a FileDelete job to remove the uploaded temp file and wraps the underlying error as a 400 Bad Request with 'Unable to process profile image', preserving the original cause.
Solutions
- Inspect the `cause` of the error / server logs to see the underlying processing failure (decode error, unsupported format, etc.).
- Re-upload a valid, standard JPEG or PNG file that opens in an image viewer.
- Convert unsupported formats (HEIC, etc.) to JPEG/PNG before uploading.
- On self-hosted servers, ensure required image codec libraries (e.g. libvips/heif) are installed in the container.
- Retry after confirming the upload completed fully (check file size, stable connection).
Defensive patterns
Strategy: validation
Validate before calling
const isValidImage = (file: File) =>
file.size > 0 && ['image/jpeg', 'image/png', 'image/webp'].includes(file.type);
if (!isValidImage(file)) throw new Error('upload a valid JPEG/PNG/WebP image'); Type guard
const isImageFile = (f: unknown): f is File & { type: 'image/jpeg' | 'image/png' | 'image/webp' } =>
f instanceof File && /^image\/(jpeg|png|webp)$/.test(f.type); Try / catch
try {
await api.createProfileImage(file);
} catch (e) {
if (e instanceof BadRequestException && e.message === 'Unable to process profile image') {
logger.warn('profile image rejected', { cause: e.cause });
// fall back / ask user for a different file
}
} Prevention
- Validate MIME type and non-zero size before upload.
- Convert HEIC/AVIF to JPEG client-side before uploading.
- Verify the image opens in a viewer/library before sending.
- Ensure the server image has required codec libraries installed.
When it happens
Trigger: POST /users/profile-image with a file whose processing (via config, userId, file.path into the profile image pipeline) throws — e.g. corrupt image, unsupported format, truncated upload, or processing-library failure.
Common situations: Uploading a file renamed to .jpg that is not actually an image; uploading HEIC/AVIF on a build without the required decoder libraries; zero-byte or interrupted downloads; images exceeding processing limits.
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
- Can't delete a missing profile Image
- Invalid backup name!
- Quota has been exceeded!
- Unsupported file type
- Asset does not have valid dimensions
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/068f9809783489e0.
Report an issue: GitHub.
Appendix: 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 e55ac299a4)