Mobile backup re-uploads deleted duplicates after 30 days because Immich does not remember excluded hashes #7363

Closed
opened 2026-02-20 05:02:25 -05:00 by deekerman · 1 comment
Owner

Originally created by @hodlemperor on GitHub (Nov 14, 2025).

I have searched the existing issues, both open and closed, to make sure this is not a duplicate report.

  • Yes

The bug

Description

When merging duplicates between Internal Upload Library and an External Library, a common scenario is:

  1. keep the asset from the External Library
  2. delete the duplicate from the Upload Library

This is a valid workflow and works at first.
However, with iOS/Android backup enabled, this creates a critical re-upload loop.

What happens

  1. You delete the upload-based duplicate → it goes to Trash
  2. After 30 days, Immich permanently removes it
  3. The mobile client checks the device library
  4. Since the file no longer exists on the server, backup logic considers it "missing"
  5. The device re-uploads it
  6. Immich creates the duplicate again
  7. User must merge again
  8. After 30 days → loop repeats

Why this happens

Immich currently does not remember the hash of uploads that were intentionally deleted by the user.
From the mobile client’s perspective, the upload is “missing” and must be synced again.

There is no mechanism to mark a file as:

“Intentionally excluded — do not re-upload this asset in the future.”

Expected behavior

Immich should:

  • keep a record of hashes explicitly deleted by the user
  • prevent backup clients from re-uploading these files
  • allow the user to override this behavior if needed
  • optionally show a warning if the user tries to upload such a file manually

User-facing options that would help

When deleting an asset or merging duplicates:

  • Delete permanently and prevent future uploads of this file
  • Delete, but allow future uploads normally

When manually uploading a file whose hash is excluded:

  • Immich should warn:

    “This file was previously excluded. Import anyway?”

Impact

This prevents:

  • selecting the External Library asset as the canonical version
  • deleting the internal duplicate
  • using mobile backup without infinite loops

This issue is separate from deduplication and requires server-side tracking of “excluded” hashes.

The OS that Immich Server is running on

Ubuntu 24.04.3 LTS

Version of Immich Server

v2.2.3

Version of Immich Mobile App

2.2.3 build.235

Platform with the issue

  • Server
  • Web
  • Mobile

Device make and model

iphone 17 ios 26.1

Your docker-compose.yml content

name: immich
services:
  immich-server:
    container_name: immich_server
    image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
    volumes:
      - ${UPLOAD_LOCATION}:/data
      - ${EXTERNAL_LOCATION}:/external:ro
      - /etc/localtime:/etc/localtime:ro
    env_file:
      - .env
    ports:
      - '2283:2283'
    depends_on:
      - redis
      - database
    restart: always
    networks:
      - proxy

  immich-machine-learning:
    container_name: immich_machine_learning
    image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
    volumes:
      - model-cache:/cache
    env_file:
      - .env
    restart: always
    networks:
      - proxy

  redis:
    container_name: immich_redis
    image: docker.io/valkey/valkey:8
    restart: always
    networks:
      - proxy

  database:
    container_name: immich_postgres
    image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_DB: ${DB_DATABASE_NAME}
    volumes:
      - ${DB_DATA_LOCATION}:/var/lib/postgresql/data
    restart: always
    networks:
      - proxy

volumes:
  model-cache:
networks:
  proxy:
    external: true

Your .env content

# You can find documentation for all the supported env variables at https://docs.immich.app/install/environment-variables

# The location where your uploaded files are stored

UPLOAD_LOCATION=/mnt/Volume_103/immich
EXTERNAL_LOCATION=/mnt/Volume_103/import

# The location where your database files are stored. Network shares are not supported for the database
DB_DATA_LOCATION=./postgres

# To set a timezone, uncomment the next line and change Etc/UTC to a TZ identifier from this list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
# TZ=Etc/UTC
TZ=Europe/Rome

# The Immich version to use. You can pin this to a specific version like "v2.1.0"
IMMICH_VERSION=v2

# Connection secret for postgres. You should change it to a random password
# Please use only the characters `A-Za-z0-9`, without special characters or spaces
DB_PASSWORD=hidden

# The values below this line do not need to be changed
###################################################################################
DB_USERNAME=postgres
DB_DATABASE_NAME=immich

#IMMICH_LOG_LEVEL=debug

Reproduction steps

  1. Enable mobile backup/sync on iOS for a user.
  2. Make sure the device contains a photo that also exists in an External Library indexed by Immich (same file content).
  3. Let the mobile app upload the photo to Immich (or upload it manually to the Upload Library).
  4. Trigger a duplicate detection/merge in the UI so that:
    • one asset points to the Upload Library file
    • one asset points to the External Library file
  5. In the merge dialog, choose to:
    • keep the External Library asset
    • delete the Upload Library asset (the upload goes to Trash).
  6. Wait until the Trash retention period expires (or manually trigger permanent deletion, if available), so that the Upload Library asset is completely removed from the server.
  7. Open the mobile app and let it run the backup/sync again.
  8. Observe that:
    • the mobile client uploads the same photo again,
    • Immich creates a new Upload Library asset,
    • the duplicate reappears, even though the External Library asset already exists.
  9. Repeat the merge and deletion and observe that the same re-upload behavior happens after every Trash expiration → effectively an infinite loop.

Relevant log output


Additional information

No response

Originally created by @hodlemperor on GitHub (Nov 14, 2025). ### I have searched the existing issues, both open and closed, to make sure this is not a duplicate report. - [x] Yes ### The bug ## **Description** When merging duplicates between Internal Upload Library and an External Library, a common scenario is: 1. keep the asset from the External Library 2. delete the duplicate from the Upload Library This is a valid workflow and works at first. However, with iOS/Android backup enabled, this creates a **critical re-upload loop**. ### **What happens** 1. You delete the upload-based duplicate → it goes to Trash 2. After 30 days, Immich permanently removes it 3. The mobile client checks the device library 4. Since the file no longer exists on the server, backup logic considers it "missing" 5. The device re-uploads it 6. Immich creates the duplicate again 7. User must merge again 8. After 30 days → loop repeats ### **Why this happens** Immich currently **does not remember the hash** of uploads that were intentionally deleted by the user. From the mobile client’s perspective, the upload is “missing” and must be synced again. There is no mechanism to mark a file as: > “Intentionally excluded — do not re-upload this asset in the future.” ### **Expected behavior** Immich should: * keep a record of hashes explicitly deleted by the user * prevent backup clients from re-uploading these files * allow the user to override this behavior if needed * optionally show a warning if the user tries to upload such a file manually ### **User-facing options that would help** When deleting an asset or merging duplicates: * **Delete permanently and prevent future uploads of this file** * **Delete, but allow future uploads normally** When manually uploading a file whose hash is excluded: * Immich should warn: > “This file was previously excluded. Import anyway?” ### **Impact** This prevents: * selecting the External Library asset as the canonical version * deleting the internal duplicate * using mobile backup without infinite loops This issue is separate from deduplication and requires server-side tracking of “excluded” hashes. ### The OS that Immich Server is running on Ubuntu 24.04.3 LTS ### Version of Immich Server v2.2.3 ### Version of Immich Mobile App 2.2.3 build.235 ### Platform with the issue - [x] Server - [x] Web - [x] Mobile ### Device make and model iphone 17 ios 26.1 ### Your docker-compose.yml content ```YAML name: immich services: immich-server: container_name: immich_server image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release} volumes: - ${UPLOAD_LOCATION}:/data - ${EXTERNAL_LOCATION}:/external:ro - /etc/localtime:/etc/localtime:ro env_file: - .env ports: - '2283:2283' depends_on: - redis - database restart: always networks: - proxy immich-machine-learning: container_name: immich_machine_learning image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release} volumes: - model-cache:/cache env_file: - .env restart: always networks: - proxy redis: container_name: immich_redis image: docker.io/valkey/valkey:8 restart: always networks: - proxy database: container_name: immich_postgres image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0 environment: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_USER: ${DB_USERNAME} POSTGRES_DB: ${DB_DATABASE_NAME} volumes: - ${DB_DATA_LOCATION}:/var/lib/postgresql/data restart: always networks: - proxy volumes: model-cache: networks: proxy: external: true ``` ### Your .env content ```Shell # You can find documentation for all the supported env variables at https://docs.immich.app/install/environment-variables # The location where your uploaded files are stored UPLOAD_LOCATION=/mnt/Volume_103/immich EXTERNAL_LOCATION=/mnt/Volume_103/import # The location where your database files are stored. Network shares are not supported for the database DB_DATA_LOCATION=./postgres # To set a timezone, uncomment the next line and change Etc/UTC to a TZ identifier from this list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List # TZ=Etc/UTC TZ=Europe/Rome # The Immich version to use. You can pin this to a specific version like "v2.1.0" IMMICH_VERSION=v2 # Connection secret for postgres. You should change it to a random password # Please use only the characters `A-Za-z0-9`, without special characters or spaces DB_PASSWORD=hidden # The values below this line do not need to be changed ################################################################################### DB_USERNAME=postgres DB_DATABASE_NAME=immich #IMMICH_LOG_LEVEL=debug ``` ### Reproduction steps 1. Enable mobile backup/sync on iOS for a user. 2. Make sure the device contains a photo that also exists in an External Library indexed by Immich (same file content). 3. Let the mobile app upload the photo to Immich (or upload it manually to the Upload Library). 4. Trigger a duplicate detection/merge in the UI so that: - one asset points to the Upload Library file - one asset points to the External Library file 5. In the merge dialog, choose to: - **keep the External Library asset** - **delete the Upload Library asset** (the upload goes to Trash). 6. Wait until the Trash retention period expires (or manually trigger permanent deletion, if available), so that the Upload Library asset is completely removed from the server. 7. Open the mobile app and let it run the backup/sync again. 8. Observe that: - the mobile client uploads the same photo again, - Immich creates a new Upload Library asset, - the duplicate reappears, even though the External Library asset already exists. 9. Repeat the merge and deletion and observe that the same re-upload behavior happens after every Trash expiration → effectively an infinite loop. ### Relevant log output ```shell ``` ### Additional information _No response_
Author
Owner

@mmomjian commented on GitHub (Nov 14, 2025):

#4282

@mmomjian commented on GitHub (Nov 14, 2025): #4282
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/immich#7363
No description provided.