New Timeline on iOS reloads on every app launch and does not use local thumbnail cache (performance unusable with large libraries) #7502

Closed
opened 2026-02-20 05:03:59 -05:00 by deekerman · 8 comments
Owner

Originally created by @gubbel-alt on GitHub (Dec 1, 2025).

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

  • Yes

The bug

Using the new timeline in Immich iOS causes the entire timeline to reload every time the app is opened. With large libraries (~100,000 assets), the loading process becomes extremely slow and results in empty placeholders for several seconds before thumbnails appear. The old timeline does not suffer from this and remains fast and responsive.

This issue appears regardless of whether “Prefer Server Images” is enabled or disabled.

The Immich web interface is not affected, and loads instantly.

Because the old timeline is planned for removal (likely in 2.4), this regression will make the iOS app very difficult to use for anyone with a large library.


Observed behavior

  • Opening the app:
    • 5–10 seconds before the timeline layout becomes visible (empty gray placeholders).
    • Additional 3–10 seconds before thumbnails appear.
  • Thumbnails only load after the timeline query completes, even though the app’s cache grows to multiple gigabytes.
  • Scrolling is laggy because thumbnails must be fetched again instead of coming from cache.
  • Offline mode: thumbnails are not immediately available (unlike the old timeline).

Expected behavior

  • Thumbnails should load instantly from a persistent local cache.
  • The timeline should not query the entire library on each app launch.
  • Large libraries should remain responsive offline and online.
  • Scrolling should not trigger repeated thumbnail fetches.

Environment

  • iOS App: 2.3.0 (build 236)
  • Immich Server: 2.3.1
  • Library size: ~100,000 assets
  • Device cache size: several GB (but not effectively used)

Relevant logs

Error getting user information from the server [CATCH ALL]
TimeoutException after 0:00:07.000000: Future not completed
#0 Future.timeout. (dart:async/future_impl.dart:1042)

#1 AuthNotifier.saveAuthInfo (package:immich_mobile/providers/auth.provider.dart:134)

#2 SplashScreenPageState.resumeSession. (package:immich_mobile/pages/common/splash_screen.page.dart:60)

This log appears consistently when the app attempts to load user data and timeline metadata on launch. The timeout seems to delay the timeline rendering and may contribute to the empty placeholders.

Additional notes

  • The old timeline works perfectly and makes full use of on-device caching.
  • The new timeline appears not to use the persistent cache, forcing a full reload each time.
  • With the old timeline slated for removal, this becomes a critical usability issue for large libraries.

Request

Please restore efficient local thumbnail caching and avoid full timeline reloads on each app launch. The behavior should match the responsiveness of the old timeline, especially for large libraries and offline scenarios.

Thank you for your work on Immich — this would greatly improve the iOS experience before the old timeline is removed.

The OS that Immich Server is running on

Portainer on Synology (Docker on Synology DSM)

Version of Immich Server

2.3.1

Version of Immich Mobile App

2.3.0 (build 236)

Platform with the issue

  • Server
  • Web
  • Mobile

Device make and model

iPhone 15 Pro Max

Your docker-compose.yml content

services:
  immich-redis:
    image: redis
    container_name: Immich-REDIS
    hostname: immich-redis
    security_opt:
      - no-new-privileges:true
    healthcheck:
      test: ["CMD-SHELL", "redis-cli ping || exit 1"]
    user: 1001:100
    environment:
      - TZ=Europe/Berlin
    volumes:
      - /volume1/docker/immich/redis:/data:rw
    restart: on-failure:5

  immich-db:
    image: ghcr.io/immich-app/postgres:16-vectorchord0.4.3-pgvectors0.2.0
    container_name: Immich-DB
    hostname: immich-db
    security_opt:
      - no-new-privileges:true
    healthcheck:
      test: ["CMD", "pg_isready", "-q", "-d", "immich", "-U", "immichuser"]
      interval: 10s
      timeout: 5s
      retries: 5
    shm_size: 128mb
    volumes:
      - /volume1/docker/immich/db:/var/lib/postgresql/data:rw
    environment:
      - TZ=Europe/Berlin
      - POSTGRES_DB=immich
      - POSTGRES_USER=***
      - POSTGRES_PASSWORD=***
      - DB_STORAGE_TYPE=HDD #Remove the red # in front of the – DB_STORAGE_TYPE=HDD if your database isn’t stored on SSDs.
    restart: on-failure:5

  immich-server:
    image: ghcr.io/immich-app/immich-server:release
    container_name: Immich-SERVER
    hostname: immich-server
    user: 1001:100
    security_opt:
      - no-new-privileges:true
    env_file:
      - stack.env
    ports:
      - 2283:2283
    volumes:
      - /volume3/Immich1/upload:/data:rw

    restart: on-failure:5
    depends_on:
      immich-redis:
        condition: service_healthy
      immich-db:
        condition: service_started

  immich-machine-learning:
    image: ghcr.io/immich-app/immich-machine-learning:release
    container_name: Immich-LEARNING
    hostname: immich-machine-learning
    user: 1001:100
    security_opt:
      - no-new-privileges:true
    env_file:
      - stack.env
    volumes:
      - /volume3/Immich1/upload:/data:rw
      - /volume1/docker/immich/cache:/cache:rw
      - /volume1/docker/immich/cache:/.cache:rw
      - /volume1/docker/immich/cache:/.config:rw
      - /volume1/docker/immich/matplotlib:/matplotlib:rw
    environment:
      - MPLCONFIGDIR=/matplotlib
    restart: on-failure:5
    depends_on:
      immich-db:
        condition: service_started

Your .env content

.env

Reproduction steps

  1. Open the Immich iOS app (new timeline enabled).
    2. Log in and let the initial sync complete.
    3. Close the app completely (swipe it away).
    4. Reopen the app.
    5. Observe that the timeline stays empty (gray placeholders) for several seconds before thumbnails start appearing.
    6. Scroll down the timeline → thumbnails continue loading slowly instead of coming from local cache.
    7. Repeat the same steps with the old timeline enabled → thumbnails load instantly.

This is reproducible every time, independent of the “Prefer Server Images” setting.

Relevant log output

Error getting user information from the server [CATCH ALL]
TimeoutException after 0:00:07.000000: Future not completed
#0      Future.timeout.<anonymous closure> (dart:async/future_impl.dart:1042)
<asynchronous suspension>
#1      AuthNotifier.saveAuthInfo (package:immich_mobile/providers/auth.provider.dart:134)
<asynchronous suspension>
#2      SplashScreenPageState.resumeSession.<anonymous closure> (package:immich_mobile/pages/common/splash_screen.page.dart:60)
<asynchronous suspension>

Additional information

No response

Originally created by @gubbel-alt on GitHub (Dec 1, 2025). ### I have searched the existing issues, both open and closed, to make sure this is not a duplicate report. - [x] Yes ### The bug Using the new timeline in Immich iOS causes the entire timeline to reload every time the app is opened. With large libraries (~100,000 assets), the loading process becomes extremely slow and results in empty placeholders for several seconds before thumbnails appear. The old timeline does not suffer from this and remains fast and responsive. This issue appears **regardless of whether “Prefer Server Images” is enabled or disabled**. The Immich web interface is **not affected**, and loads instantly. Because the old timeline is planned for removal (likely in 2.4), this regression will make the iOS app very difficult to use for anyone with a large library. --- ### **Observed behavior** - Opening the app: - 5–10 seconds before the timeline layout becomes visible (empty gray placeholders). - Additional 3–10 seconds before thumbnails appear. - Thumbnails only load after the timeline query completes, even though the app’s cache grows to multiple gigabytes. - Scrolling is laggy because thumbnails must be fetched again instead of coming from cache. - Offline mode: thumbnails are not immediately available (unlike the old timeline). --- ### **Expected behavior** - Thumbnails should load instantly from a persistent local cache. - The timeline should not query the entire library on each app launch. - Large libraries should remain responsive offline and online. - Scrolling should not trigger repeated thumbnail fetches. --- ### **Environment** - iOS App: 2.3.0 (build 236) - Immich Server: 2.3.1 - Library size: ~100,000 assets - Device cache size: several GB (but not effectively used) --- ### **Relevant logs** Error getting user information from the server [CATCH ALL] TimeoutException after 0:00:07.000000: Future not completed #0 Future.timeout.<anonymous closure> (dart:async/future_impl.dart:1042) <asynchronous suspension> #1 AuthNotifier.saveAuthInfo (package:immich_mobile/providers/auth.provider.dart:134) <asynchronous suspension> #2 SplashScreenPageState.resumeSession.<anonymous closure> (package:immich_mobile/pages/common/splash_screen.page.dart:60) <asynchronous suspension> This log appears consistently when the app attempts to load user data and timeline metadata on launch. The timeout seems to delay the timeline rendering and may contribute to the empty placeholders. ### **Additional notes** - The old timeline works perfectly and makes full use of on-device caching. - The new timeline appears not to use the persistent cache, forcing a full reload each time. - With the old timeline slated for removal, this becomes a critical usability issue for large libraries. ### **Request** Please restore efficient local thumbnail caching and avoid full timeline reloads on each app launch. The behavior should match the responsiveness of the old timeline, especially for large libraries and offline scenarios. Thank you for your work on Immich — this would greatly improve the iOS experience before the old timeline is removed. ### The OS that Immich Server is running on Portainer on Synology (Docker on Synology DSM) ### Version of Immich Server 2.3.1 ### Version of Immich Mobile App 2.3.0 (build 236) ### Platform with the issue - [ ] Server - [ ] Web - [x] Mobile ### Device make and model iPhone 15 Pro Max ### Your docker-compose.yml content ```YAML services: immich-redis: image: redis container_name: Immich-REDIS hostname: immich-redis security_opt: - no-new-privileges:true healthcheck: test: ["CMD-SHELL", "redis-cli ping || exit 1"] user: 1001:100 environment: - TZ=Europe/Berlin volumes: - /volume1/docker/immich/redis:/data:rw restart: on-failure:5 immich-db: image: ghcr.io/immich-app/postgres:16-vectorchord0.4.3-pgvectors0.2.0 container_name: Immich-DB hostname: immich-db security_opt: - no-new-privileges:true healthcheck: test: ["CMD", "pg_isready", "-q", "-d", "immich", "-U", "immichuser"] interval: 10s timeout: 5s retries: 5 shm_size: 128mb volumes: - /volume1/docker/immich/db:/var/lib/postgresql/data:rw environment: - TZ=Europe/Berlin - POSTGRES_DB=immich - POSTGRES_USER=*** - POSTGRES_PASSWORD=*** - DB_STORAGE_TYPE=HDD #Remove the red # in front of the – DB_STORAGE_TYPE=HDD if your database isn’t stored on SSDs. restart: on-failure:5 immich-server: image: ghcr.io/immich-app/immich-server:release container_name: Immich-SERVER hostname: immich-server user: 1001:100 security_opt: - no-new-privileges:true env_file: - stack.env ports: - 2283:2283 volumes: - /volume3/Immich1/upload:/data:rw restart: on-failure:5 depends_on: immich-redis: condition: service_healthy immich-db: condition: service_started immich-machine-learning: image: ghcr.io/immich-app/immich-machine-learning:release container_name: Immich-LEARNING hostname: immich-machine-learning user: 1001:100 security_opt: - no-new-privileges:true env_file: - stack.env volumes: - /volume3/Immich1/upload:/data:rw - /volume1/docker/immich/cache:/cache:rw - /volume1/docker/immich/cache:/.cache:rw - /volume1/docker/immich/cache:/.config:rw - /volume1/docker/immich/matplotlib:/matplotlib:rw environment: - MPLCONFIGDIR=/matplotlib restart: on-failure:5 depends_on: immich-db: condition: service_started ``` ### Your .env content ```Shell .env ``` ### Reproduction steps 1. Open the Immich iOS app (new timeline enabled). 2. Log in and let the initial sync complete. 3. Close the app completely (swipe it away). 4. Reopen the app. 5. Observe that the timeline stays empty (gray placeholders) for several seconds before thumbnails start appearing. 6. Scroll down the timeline → thumbnails continue loading slowly instead of coming from local cache. 7. Repeat the same steps with the old timeline enabled → thumbnails load instantly. This is reproducible every time, independent of the “Prefer Server Images” setting. ### Relevant log output ```shell Error getting user information from the server [CATCH ALL] TimeoutException after 0:00:07.000000: Future not completed #0 Future.timeout.<anonymous closure> (dart:async/future_impl.dart:1042) <asynchronous suspension> #1 AuthNotifier.saveAuthInfo (package:immich_mobile/providers/auth.provider.dart:134) <asynchronous suspension> #2 SplashScreenPageState.resumeSession.<anonymous closure> (package:immich_mobile/pages/common/splash_screen.page.dart:60) <asynchronous suspension> ``` ### Additional information _No response_
Author
Owner

@alextran1502 commented on GitHub (Dec 1, 2025):

How is the behavior if you are connecting via direct ip address?

@alextran1502 commented on GitHub (Dec 1, 2025): How is the behavior if you are connecting via direct ip address?
Author
Owner

@gubbel-alt commented on GitHub (Dec 1, 2025):

I am currently using the app only via direct local IP, so I cannot compare it to any other connection method.
I can only confirm that the issue occurs when connected directly via the local IP.

@gubbel-alt commented on GitHub (Dec 1, 2025): I am currently using the app only via direct local IP, so I cannot compare it to any other connection method. I can only confirm that the issue occurs when connected directly via the local IP.
Author
Owner
@gubbel-alt commented on GitHub (Dec 1, 2025): https://github.com/user-attachments/assets/94fe4939-3266-4edf-a5e3-95b669caf148
Author
Owner

@gubbel-alt commented on GitHub (Dec 1, 2025):

https://github.com/user-attachments/assets/fb57e869-c404-403d-9344-d1051fe5b0c3

I uploaded a screen recording that shows how long the new timeline takes to load. This happens every time the app is reopened.

@gubbel-alt commented on GitHub (Dec 1, 2025): https://github.com/user-attachments/assets/fb57e869-c404-403d-9344-d1051fe5b0c3 I uploaded a screen recording that shows how long the new timeline takes to load. This happens every time the app is reopened.
Author
Owner

@bo0tzz commented on GitHub (Dec 1, 2025):

Are you certain the app is successfully connecting to your server without any interference?

@bo0tzz commented on GitHub (Dec 1, 2025): Are you certain the app is successfully connecting to your server without any interference?
Author
Owner

@alextran1502 commented on GitHub (Dec 1, 2025):

Can you try logout and log back in or use the demo instance (double tapping on the spinning logo on the login screen) and see if the problem persists. From what I am seeing, the app isn't able to connect to your instance so it cannot make any request

@alextran1502 commented on GitHub (Dec 1, 2025): Can you try logout and log back in or use the demo instance (double tapping on the spinning logo on the login screen) and see if the problem persists. From what I am seeing, the app isn't able to connect to your instance so it cannot make any request
Author
Owner

@gubbel-alt commented on GitHub (Dec 3, 2025):

Thank you very much for your help!

I have additional information regarding the behavior:
• The screen recordings I uploaded are trimmed exactly at the moment the original photos appear, so no private images are visible.
• The photos do appear in the app, but it takes a very long time for them to load.
• Every time the app is closed and reopened, all thumbnails must be reloaded, even though the cache on the device grows to several GB. It seems the cache is not being utilized at all.
• I have already tested full logout and login — the issue persists.
• I am using the app directly via local IP; the app is able to connect, but the new timeline still reloads slowly and does not use the cache.
• The demo instance works fine; the problem is only with my local instance.

This makes the iOS app very difficult to use with large libraries (~100,000 assets), especially since the old timeline (which worked fine) will soon be removed.

Two errors are from the app log, one from the Docker log:


[Nest] 7 - 12/03/2025, 3:40:47 PM ERROR [Microservices:LibraryService] Library watcher for library XXX encountered error: Error: ENOSPC: System limit for number of file watchers reached, watch '/XXX/Drive/7.jpg/SYNOINDEX_MEDIA_INFO'


Error in runInIsolateGentle for remote-sync

ClientException: Connection closed while receiving data, uri=http://10.1.1.10:2283/api/sync/stream

#0 _HttpIncoming.listen. (dart:_http/http_impl.dart:438)
#1 Stream.handleError. (dart:async/stream.dart:961)
#2 _HandleErrorStream._handleError (dart:async/stream_pipe.dart:303)
#3 _ForwardingStreamSubscription._handleError (dart:async/stream_pipe.dart:188)
#4 _rootRunBinary (dart:async/zone.dart:1560)
#5 _CustomZone.runBinary (dart:async/zone.dart:1436)
#6 _CustomZone.runBinaryGuarded (dart:async/zone.dart:1337)
#7 _BufferingStreamSubscription._sendError.sendError (dart:async/stream_impl.dart:400)
#8 _BufferingStreamSubscription._sendError (dart:async/stream_impl.dart:418)
#9 _DelayedError.perform (dart:async/stream_impl.dart:584)
#10 _PendingEvents.handleNext (dart:async/stream_impl.dart:678)
#11 _PendingEvents.schedule. (dart:async/stream_impl.dart:649)
#12 _rootRun (dart:async/zone.dart:1517)
#13 _CustomZone.run (dart:async/zone.dart:1422)
#14 _CustomZone.runGuarded (dart:async/zone.dart:1321)
#15 _CustomZone.bindCallbackGuarded. (dart:async/zone.dart:1362)
#16 _rootRun (dart:async/zone.dart:1525)
#17 _CustomZone.run (dart:async/zone.dart:1422)
#18 _CustomZone.runGuarded (dart:async/zone.dart:1321)
#19 _CustomZone.bindCallbackGuarded. (dart:async/zone.dart:1362)
#20 _microtaskLoop (dart:async/schedule_microtask.dart:40)
#21 _startMicrotaskLoop (dart:async/schedule_microtask.dart:49)
#22 _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:127)
#23 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:194)


Error getting user information from the server [CATCH ALL]
TimeoutException after 0:00:07.000000: Future not completed
#0 Future.timeout. (dart:async/future_impl.dart:1042)

#1 AuthNotifier.saveAuthInfo (package:immich_mobile/providers/auth.provider.dart:134)

#2 SplashScreenPageState.resumeSession. (package:immich_mobile/pages/common/splash_screen.page.dart:60)

@gubbel-alt commented on GitHub (Dec 3, 2025): Thank you very much for your help! I have additional information regarding the behavior: • The screen recordings I uploaded are trimmed exactly at the moment the original photos appear, so no private images are visible. • The photos do appear in the app, but it takes a very long time for them to load. • Every time the app is closed and reopened, all thumbnails must be reloaded, even though the cache on the device grows to several GB. It seems the cache is not being utilized at all. • I have already tested full logout and login — the issue persists. • I am using the app directly via local IP; the app is able to connect, but the new timeline still reloads slowly and does not use the cache. • The demo instance works fine; the problem is only with my local instance. This makes the iOS app very difficult to use with large libraries (~100,000 assets), especially since the old timeline (which worked fine) will soon be removed. Two errors are from the app log, one from the Docker log: ________ [Nest] 7 - 12/03/2025, 3:40:47 PM ERROR [Microservices:LibraryService] Library watcher for library XXX encountered error: Error: ENOSPC: System limit for number of file watchers reached, watch '/XXX/Drive/7.jpg/SYNOINDEX_MEDIA_INFO' ________ Error in runInIsolateGentle for remote-sync ClientException: Connection closed while receiving data, uri=http://10.1.1.10:2283/api/sync/stream #0 _HttpIncoming.listen.<anonymous closure> (dart:_http/http_impl.dart:438) #1 Stream.handleError.<anonymous closure> (dart:async/stream.dart:961) #2 _HandleErrorStream._handleError (dart:async/stream_pipe.dart:303) #3 _ForwardingStreamSubscription._handleError (dart:async/stream_pipe.dart:188) #4 _rootRunBinary (dart:async/zone.dart:1560) #5 _CustomZone.runBinary (dart:async/zone.dart:1436) #6 _CustomZone.runBinaryGuarded (dart:async/zone.dart:1337) #7 _BufferingStreamSubscription._sendError.sendError (dart:async/stream_impl.dart:400) #8 _BufferingStreamSubscription._sendError (dart:async/stream_impl.dart:418) #9 _DelayedError.perform (dart:async/stream_impl.dart:584) #10 _PendingEvents.handleNext (dart:async/stream_impl.dart:678) #11 _PendingEvents.schedule.<anonymous closure> (dart:async/stream_impl.dart:649) #12 _rootRun (dart:async/zone.dart:1517) #13 _CustomZone.run (dart:async/zone.dart:1422) #14 _CustomZone.runGuarded (dart:async/zone.dart:1321) #15 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1362) #16 _rootRun (dart:async/zone.dart:1525) #17 _CustomZone.run (dart:async/zone.dart:1422) #18 _CustomZone.runGuarded (dart:async/zone.dart:1321) #19 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1362) #20 _microtaskLoop (dart:async/schedule_microtask.dart:40) #21 _startMicrotaskLoop (dart:async/schedule_microtask.dart:49) #22 _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:127) #23 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:194) ________ Error getting user information from the server [CATCH ALL] TimeoutException after 0:00:07.000000: Future not completed #0 Future.timeout.<anonymous closure> (dart:async/future_impl.dart:1042) <asynchronous suspension> #1 AuthNotifier.saveAuthInfo (package:immich_mobile/providers/auth.provider.dart:134) <asynchronous suspension> #2 SplashScreenPageState.resumeSession.<anonymous closure> (package:immich_mobile/pages/common/splash_screen.page.dart:60) <asynchronous suspension>
Author
Owner

@alextran1502 commented on GitHub (Dec 3, 2025):

The issue is not from the app but from exceeding the CPU/IO usage on your server from the file-watcher feature. You can refer to the following links to increase the limit

https://stackoverflow.com/questions/53930305/nodemon-error-system-limit-for-number-of-file-watchers-reached

You can also try disable the file watcher feature of the external library and see if the situation improves.

FWIF, my library has ~100,000 assets, and the mobile app loads the data quickly. As you stated with the demo instance

@alextran1502 commented on GitHub (Dec 3, 2025): The issue is not from the app but from exceeding the CPU/IO usage on your server from the file-watcher feature. You can refer to the following links to increase the limit https://stackoverflow.com/questions/53930305/nodemon-error-system-limit-for-number-of-file-watchers-reached You can also try disable the file watcher feature of the external library and see if the situation improves. FWIF, my library has ~100,000 assets, and the mobile app loads the data quickly. As you stated with the demo instance
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#7502
No description provided.