File descriptor/handle left open on upload fail #8320

Open
opened 2026-02-05 13:39:36 +03:00 by OVERLORD · 0 comments
Owner

Originally created by @goalie2002 on GitHub (Jan 28, 2026).

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

  • Yes

The bug

When an upload to the server is interrupted, for example by a spotty network connection, the file descriptor is not closed after the connection errors out. Instead it remains open, even after the file is eventually cleaned up (which unlinks it).

On a local (ext4) filesystem this works ok, as the file immediately disappears from the directory tree after unlinking, even though the FD is still open. On an SMB network share, however, this results in the file ending up with 0 links, while still "existing" in the directory until the FD is closed. This causes access errors if any other program (such as a backup service) tries to read it.

The OS that Immich Server is running on

Ubuntu 24.04 LTS

Version of Immich Server

v2.5.0

Version of Immich Mobile App

N/A

Platform with the issue

  • Server
  • Web
  • Mobile

Device make and model

No response

Your docker-compose.yml content

name: immich_main

services:
  immich-server:
    image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
    extends: # uncomment this section for hardware acceleration - see https://immich.app/docs/features/hardware-transcoding
      file: hwaccel.transcoding.yml
      service: nvenc # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
    volumes:
      - ${UPLOAD_LOCATION}:/data
      - ${THUMBNAIL_PATH}:/data/thumbs
      - ${SYNOLOGY_EXTERNAL_PATH}:/usr/src/app/external:ro
      - /etc/localtime:/etc/localtime:ro
    env_file:
      - .env
    ports:
      - 2283:2283
    depends_on:
      - redis
      - database
    networks:
      - immich-main
      - immich-shared
    restart: always
    healthcheck:
      disable: false

  redis:
    image: docker.io/valkey/valkey:8-bookworm@sha256:fea8b3e67b15729d4bb70589eb03367bab9ad1ee89c876f54327fc7c6e618571
    healthcheck:
      test: redis-cli ping || exit 1
    networks:
      - immich-main
    restart: always

  database:
    container_name: immich_postgres
    image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_DB: ${DB_DATABASE_NAME}
      POSTGRES_INITDB_ARGS: '--data-checksums'
      # Uncomment the DB_STORAGE_TYPE: 'HDD' var if your database isn't stored on SSDs
      # DB_STORAGE_TYPE: 'HDD
    volumes:
      # Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file
      - ${DB_DATA_LOCATION}:/var/lib/postgresql/data
    shm_size: 128mb
    networks:
      - immich-main
    restart: always
    
networks:
  immich-main:
  immich-shared:
    external: true

Your .env content

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

# The location where your uploaded files are stored
UPLOAD_LOCATION=/media/Synology/immich_photo_library

# The location where your database files are stored
DB_DATA_LOCATION=./postgres

# The Immich version to use. You can pin this to a specific version like "v1.71.0"
IMMICH_VERSION=release

# 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=Canada/Pacific

# Connection secret for postgres. You should change it to a random password
DB_PASSWORD=

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


###################################################################################
SYNOLOGY_EXTERNAL_PATH=

Reproduction steps

  1. Initiate an upload (i.e. on the mobile app)
  2. Cause a network interruption (disable wifi and data on the phone)
  3. Wait until the connection errors out and the file gets cleaned up (log: "Request error while uploading file, cleaning up")
  4. Check the open FDs of the immich process with 'lsof -p {PID}'. Note that the FD for the file that was just partially uploaded is still open. (On SMB mount) you can also use the 'stat' command on the file and observe that the link count is 0.
  5. (Optional) Stop immich and notice that (on SMB mount) the file now disappears as there are no more open FDs pointing to it.

Relevant log output

N/A (nothing helpful shows up in the log)

Additional information

Digging a bit into the code, this might be a limitation of multer not closing its FD when the connection errors out.

Oberseved behaviour

Before cleanup

Lsof:
immich-ap 3993723 root 53w REG 0,61 210451229 1836530 /data/upload/87e61468-556c-4430-ad7c-1ce4af295598/a0/48/a048d851-5d25-48c7-a0a2-020bbc234dd4.MOV

Stat of file:
File: a048d851-5d25-48c7-a0a2-020bbc234dd4.MOV Size: 210451229 Blocks: 411038 IO Block: 1048576 regular file Device: 0,61 Inode: 1836530 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 1000/ server) Gid: ( 1000/ server)

After cleanup

Lsof:
immich-ap 3993723 root 53w REG 0,61 210451229 1836530 /data/upload/87e61468-556c-4430-ad7c-1ce4af295598/a0/48/a048d851-5d25-48c7-a0a2-020bbc234dd4.MOV (deleted)

Stat of file:
File: a048d851-5d25-48c7-a0a2-020bbc234dd4.MOV Size: 210451229 Blocks: 411038 IO Block: 1048576 regular file Device: 0,61 Inode: 1836530 Links: 0 Access: (0755/-rwxr-xr-x) Uid: ( 1000/ server) Gid: ( 1000/ server)

Originally created by @goalie2002 on GitHub (Jan 28, 2026). ### I have searched the existing issues, both open and closed, to make sure this is not a duplicate report. - [x] Yes ### The bug When an upload to the server is interrupted, for example by a spotty network connection, the file descriptor is not closed after the connection errors out. Instead it remains open, even after the file is eventually cleaned up (which unlinks it). On a local (ext4) filesystem this works ok, as the file immediately disappears from the directory tree after unlinking, even though the FD is still open. On an SMB network share, however, this results in the file ending up with 0 links, while still "existing" in the directory until the FD is closed. This causes access errors if any other program (such as a backup service) tries to read it. ### The OS that Immich Server is running on Ubuntu 24.04 LTS ### Version of Immich Server v2.5.0 ### Version of Immich Mobile App N/A ### Platform with the issue - [x] Server - [ ] Web - [ ] Mobile ### Device make and model _No response_ ### Your docker-compose.yml content ```YAML name: immich_main services: immich-server: image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release} extends: # uncomment this section for hardware acceleration - see https://immich.app/docs/features/hardware-transcoding file: hwaccel.transcoding.yml service: nvenc # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding volumes: - ${UPLOAD_LOCATION}:/data - ${THUMBNAIL_PATH}:/data/thumbs - ${SYNOLOGY_EXTERNAL_PATH}:/usr/src/app/external:ro - /etc/localtime:/etc/localtime:ro env_file: - .env ports: - 2283:2283 depends_on: - redis - database networks: - immich-main - immich-shared restart: always healthcheck: disable: false redis: image: docker.io/valkey/valkey:8-bookworm@sha256:fea8b3e67b15729d4bb70589eb03367bab9ad1ee89c876f54327fc7c6e618571 healthcheck: test: redis-cli ping || exit 1 networks: - immich-main restart: always database: container_name: immich_postgres image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23 environment: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_USER: ${DB_USERNAME} POSTGRES_DB: ${DB_DATABASE_NAME} POSTGRES_INITDB_ARGS: '--data-checksums' # Uncomment the DB_STORAGE_TYPE: 'HDD' var if your database isn't stored on SSDs # DB_STORAGE_TYPE: 'HDD volumes: # Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file - ${DB_DATA_LOCATION}:/var/lib/postgresql/data shm_size: 128mb networks: - immich-main restart: always networks: immich-main: immich-shared: external: true ``` ### Your .env content ```Shell # You can find documentation for all the supported env variables at https://immich.app/docs/install/environment-variables # The location where your uploaded files are stored UPLOAD_LOCATION=/media/Synology/immich_photo_library # The location where your database files are stored DB_DATA_LOCATION=./postgres # The Immich version to use. You can pin this to a specific version like "v1.71.0" IMMICH_VERSION=release # 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=Canada/Pacific # Connection secret for postgres. You should change it to a random password DB_PASSWORD= # The values below this line do not need to be changed ################################################################################### DB_USERNAME= DB_DATABASE_NAME= ################################################################################### SYNOLOGY_EXTERNAL_PATH= ``` ### Reproduction steps 1. Initiate an upload (i.e. on the mobile app) 2. Cause a network interruption (disable wifi and data on the phone) 3. Wait until the connection errors out and the file gets cleaned up (log: "Request error while uploading file, cleaning up") 4. Check the open FDs of the immich process with 'lsof -p {PID}'. Note that the FD for the file that was just partially uploaded is still open. (On SMB mount) you can also use the 'stat' command on the file and observe that the link count is 0. 5. (Optional) Stop immich and notice that (on SMB mount) the file now disappears as there are no more open FDs pointing to it. ### Relevant log output ```shell N/A (nothing helpful shows up in the log) ``` ### Additional information Digging a bit into the code, this might be a limitation of multer not closing its FD when the connection errors out. ## Oberseved behaviour ### Before cleanup Lsof: `immich-ap 3993723 root 53w REG 0,61 210451229 1836530 /data/upload/87e61468-556c-4430-ad7c-1ce4af295598/a0/48/a048d851-5d25-48c7-a0a2-020bbc234dd4.MOV ` Stat of file: ` File: a048d851-5d25-48c7-a0a2-020bbc234dd4.MOV Size: 210451229 Blocks: 411038 IO Block: 1048576 regular file Device: 0,61 Inode: 1836530 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 1000/ server) Gid: ( 1000/ server) ` ### After cleanup Lsof: `immich-ap 3993723 root 53w REG 0,61 210451229 1836530 /data/upload/87e61468-556c-4430-ad7c-1ce4af295598/a0/48/a048d851-5d25-48c7-a0a2-020bbc234dd4.MOV (deleted) ` Stat of file: ` File: a048d851-5d25-48c7-a0a2-020bbc234dd4.MOV Size: 210451229 Blocks: 411038 IO Block: 1048576 regular file Device: 0,61 Inode: 1836530 Links: 0 Access: (0755/-rwxr-xr-x) Uid: ( 1000/ server) Gid: ( 1000/ server) `
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: immich-app/immich#8320