[PR #23469] [MERGED] fix(mobile): handle empty original filename #17548

Closed
opened 2026-02-05 16:23:44 +03:00 by OVERLORD · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/immich-app/immich/pull/23469
Author: @skatsubo
Created: 11/1/2025
Status: Merged
Merged: 11/4/2025
Merged by: @alextran1502

Base: mainHead: fix/empty-original-filename


📝 Commits (3)

  • 9a77f7b Handle empty original filename
  • b02f3b5 Handle TypeError from photo_manager titleAsync
  • 85f75d8 More compact exception log

📊 Changes

1 file changed (+10 additions, -3 deletions)

View changed files

📝 mobile/lib/repositories/asset_media.repository.dart (+10 -3)

📄 Description

Description

It seems iOS (PHAssetResource / iCloud) sometimes returns empty originalFilename. This triggers two issues:

  1. TypeError from photo_manager titleAsync when getting original filename.
  2. An empty filename fails validation in background_downloader Task when creating an upload task, thus blocking upload.

This PR modifies getOriginalFilename:

  1. Adds exception handling (fix issue 1)
  2. Returns null instead of an empty string, so the caller (upload service) will fall back to asset.name if original filename is missing (fix issue 2)

Issue

Reported on Discord: Immich iOS app fails to upload when the file name is empty.

app log: TypeError from getTitleAsync
2025-11-01 20:41:52.478751 | info     | DriftBackupNotifier  | Start a new backup queue |
2025-11-01 20:41:52.478744 | info     | DriftBackupNotifier  | Found 0 tasks |
2025-11-01 20:41:52.476990 | info     | DriftBackupNotifier  | Resuming backup tasks... |
2025-11-01 20:41:50.565681 | severe   | ImmichErrorLogger    | PlatformDispatcher - Catch all | type 'Null' is not a subtype of type 'FutureOr<String>' |
#0      PhotoManagerPlugin.getTitleAsync (package:photo_manager/src/internal/plugin.dart:474)
<asynchronous suspension>
#1      AssetMediaRepository.getOriginalFilename (package:immich_mobile/repositories/asset_media.repository.dart:94)
<asynchronous suspension>
#2      UploadService.getUploadTask (package:immich_mobile/services/upload.service.dart:333)
<asynchronous suspension>
#3      UploadService.startBackup (package:immich_mobile/services/upload.service.dart:144)
<asynchronous suspension>

How Has This Been Tested?

To "reproduce" the issue I modified getTitleAsync in local photo_manager sources lib/src/internal/plugin.dart to replicate atypical scenarios:

  • trigger TypeError for some assets
  • return empty string for some assets
modified getTitleAsync
  Future<String> getTitleAsync(
    AssetEntity entity, {
    bool isOrigin = true,
    int subtype = 0,
    PMDarwinAVFileType? darwinFileType,
  }) async {
    if (Platform.isIOS || Platform.isMacOS) {
      if ('012345'.contains(entity.id[0])) {        // trigger TypeError for 30% of assets
        return await Future.value(null as String);  //
      }                                             //
      if ('6789AB'.contains(entity.id[0])) {        // return empty string for 30% of assets
        return '';                                  //
      }                                             //
      return await _channel.invokeMethod(
        PMConstants.mGetTitleAsync,
        <String, dynamic>{
          'id': entity.id,
          'subtype': subtype,
          'isOrigin': isOrigin,
          'darwinFileType': darwinFileType?.value ?? 0,
        },
      );
    }
    return entity.title ?? '';
  }

Test UI

Navigate to asset details. This triggers a call to getOriginalFilename. TypeError is handled:
flutter: [WARNING] [2025-11-02 16:00:11.058149] [AssetMediaRepository] Failed to get original filename for asset: 3E94A621-94A3-4CB9-ABEA-257DD60B6F77/L0/001. Error: type 'Null' is not a subtype of type 'String' in type cast

screenshot image

Test backup

Enable backup. Some assets trigger TypeError (caught), still they are uploaded successfully.

Log during backup for local asset id 5FEA1957...

flutter: [WARNING] [2025-11-02 21:57:58.585765] [AssetMediaRepository] Failed to get original filename for asset: 5FEA1957-BC57-49F6-A8CB-8429AEFC66E2/L0/001. Error: type 'Null' is not a subtype of type 'String' in type cast

flutter: [FINE] [2025-11-02 21:57:59.234324] [socket_io_client:engine.Socket] socket receive: type "message", data "2["on_upload_success",{"id":"17a89e6e-285f-47a8-993e-2f589d8b13fb","createdAt":"2025-11-02T18:57:58.794Z","deviceAssetId":"5FEA1957-BC57-49F6-A8CB-8429AEFC66E2/L0/001","ownerId":"7a18f962-7c77-4a35-a165-c8fbb4291e65","deviceId":"121c5007afc573ab9a25ba28b6b5be0d10d741e6fc608115ab3efb89724ae64a","libraryId":null,"type":"IMAGE","originalPath":"/data/library/admin/2024/Other/E189E325-0E21-4D8D-97A5-8214F820390C.jpg","originalFileName":"E189E325-0E21-4D8D-97A5-8214F820390C.JPG","originalMimeType":"image/jpeg","thumbhash":"XhgKHQQFinebGLeWWHeqhkyP4NQJ","fileCreatedAt":"2024-10-26T14:24:27.216Z","fileModifiedAt":"2024-11-09T14:09:31.000Z","localDateTime":"2024-10-26T17:24:27.216Z","updatedAt":"2025-11-02T18:57:59.232Z","isFavorite":false,"isArchived":false,"isTrashed":false,"visibility":"timeline","duration":"0:00:00.00000","exifInfo":{"make":"Apple","model":"iPad mini 2","exifImageWidth":1280<…>

Also, existing unit tests already cover null response from getOriginalFilename.

Checklist:

  • I have performed a self-review of my own code
  • I have no unrelated changes in the PR.
  • I have written tests for new code (if applicable)
  • I have followed naming conventions/patterns in the surrounding code
  • All code in src/repositories/ is pretty basic/simple and does not have any immich specific logic (that belongs in src/services/)

Please describe to which degree, if any, an LLM was used in creating this pull request.

Help with dart, compare fix options.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/immich-app/immich/pull/23469 **Author:** [@skatsubo](https://github.com/skatsubo) **Created:** 11/1/2025 **Status:** ✅ Merged **Merged:** 11/4/2025 **Merged by:** [@alextran1502](https://github.com/alextran1502) **Base:** `main` ← **Head:** `fix/empty-original-filename` --- ### 📝 Commits (3) - [`9a77f7b`](https://github.com/immich-app/immich/commit/9a77f7b2d85aa99a22b983cee731f9aafabe1e84) Handle empty original filename - [`b02f3b5`](https://github.com/immich-app/immich/commit/b02f3b55da146db3cf571c601ad8df5a96c08e99) Handle TypeError from photo_manager titleAsync - [`85f75d8`](https://github.com/immich-app/immich/commit/85f75d87a5dc4b33f6d44e4329b177814f333f02) More compact exception log ### 📊 Changes **1 file changed** (+10 additions, -3 deletions) <details> <summary>View changed files</summary> 📝 `mobile/lib/repositories/asset_media.repository.dart` (+10 -3) </details> ### 📄 Description ## Description It seems iOS (PHAssetResource / iCloud) sometimes returns empty `originalFilename`. This triggers two issues: 1. TypeError from [photo_manager](https://github.com/fluttercandies/flutter_photo_manager/blob/16680a07ec0e4da10923efd4e7936354f3a01c19/lib/src/internal/plugin.dart#L474) `titleAsync` when getting original filename. 2. An empty filename fails validation in [background_downloader](https://github.com/781flyingdutchman/background_downloader/blob/2859d43a0953e440b236b2de3f951955c535f888/lib/src/task.dart#L327-L329) `Task` when creating an upload task, thus blocking upload. This PR modifies `getOriginalFilename`: 1. Adds exception handling (fix issue 1) 2. Returns `null` instead of an empty string, so the caller (upload service) will fall back to `asset.name` if original filename is missing (fix issue 2) ## Issue Reported [on Discord](https://discord.com/channels/979116623879368755/1434033555314577538): Immich iOS app fails to upload when the file name is empty. <details><summary>app log: TypeError from getTitleAsync</summary> ``` 2025-11-01 20:41:52.478751 | info | DriftBackupNotifier | Start a new backup queue | 2025-11-01 20:41:52.478744 | info | DriftBackupNotifier | Found 0 tasks | 2025-11-01 20:41:52.476990 | info | DriftBackupNotifier | Resuming backup tasks... | 2025-11-01 20:41:50.565681 | severe | ImmichErrorLogger | PlatformDispatcher - Catch all | type 'Null' is not a subtype of type 'FutureOr<String>' | #0 PhotoManagerPlugin.getTitleAsync (package:photo_manager/src/internal/plugin.dart:474) <asynchronous suspension> #1 AssetMediaRepository.getOriginalFilename (package:immich_mobile/repositories/asset_media.repository.dart:94) <asynchronous suspension> #2 UploadService.getUploadTask (package:immich_mobile/services/upload.service.dart:333) <asynchronous suspension> #3 UploadService.startBackup (package:immich_mobile/services/upload.service.dart:144) <asynchronous suspension> ``` </details> ## How Has This Been Tested? To "reproduce" the issue I modified `getTitleAsync` in local photo_manager sources `lib/src/internal/plugin.dart` to replicate atypical scenarios: - trigger TypeError for some assets - return empty string for some assets <details><summary>modified getTitleAsync</summary> ```dart Future<String> getTitleAsync( AssetEntity entity, { bool isOrigin = true, int subtype = 0, PMDarwinAVFileType? darwinFileType, }) async { if (Platform.isIOS || Platform.isMacOS) { if ('012345'.contains(entity.id[0])) { // trigger TypeError for 30% of assets return await Future.value(null as String); // } // if ('6789AB'.contains(entity.id[0])) { // return empty string for 30% of assets return ''; // } // return await _channel.invokeMethod( PMConstants.mGetTitleAsync, <String, dynamic>{ 'id': entity.id, 'subtype': subtype, 'isOrigin': isOrigin, 'darwinFileType': darwinFileType?.value ?? 0, }, ); } return entity.title ?? ''; } ``` </details> ### Test UI Navigate to asset details. This triggers a call to `getOriginalFilename`. TypeError is handled: ` flutter: [WARNING] [2025-11-02 16:00:11.058149] [AssetMediaRepository] Failed to get original filename for asset: 3E94A621-94A3-4CB9-ABEA-257DD60B6F77/L0/001. Error: type 'Null' is not a subtype of type 'String' in type cast ` <details><summary>screenshot</summary> <img width="2618" height="428" alt="image" src="https://github.com/user-attachments/assets/224ea60e-4951-420f-935f-8a7c2730faa7" /> </details> ### Test backup Enable backup. Some assets trigger TypeError (caught), still they are uploaded successfully. <details><summary>Log during backup for local asset id 5FEA1957...</summary> ` flutter: [WARNING] [2025-11-02 21:57:58.585765] [AssetMediaRepository] Failed to get original filename for asset: 5FEA1957-BC57-49F6-A8CB-8429AEFC66E2/L0/001. Error: type 'Null' is not a subtype of type 'String' in type cast ` ` flutter: [FINE] [2025-11-02 21:57:59.234324] [socket_io_client:engine.Socket] socket receive: type "message", data "2["on_upload_success",{"id":"17a89e6e-285f-47a8-993e-2f589d8b13fb","createdAt":"2025-11-02T18:57:58.794Z","deviceAssetId":"5FEA1957-BC57-49F6-A8CB-8429AEFC66E2/L0/001","ownerId":"7a18f962-7c77-4a35-a165-c8fbb4291e65","deviceId":"121c5007afc573ab9a25ba28b6b5be0d10d741e6fc608115ab3efb89724ae64a","libraryId":null,"type":"IMAGE","originalPath":"/data/library/admin/2024/Other/E189E325-0E21-4D8D-97A5-8214F820390C.jpg","originalFileName":"E189E325-0E21-4D8D-97A5-8214F820390C.JPG","originalMimeType":"image/jpeg","thumbhash":"XhgKHQQFinebGLeWWHeqhkyP4NQJ","fileCreatedAt":"2024-10-26T14:24:27.216Z","fileModifiedAt":"2024-11-09T14:09:31.000Z","localDateTime":"2024-10-26T17:24:27.216Z","updatedAt":"2025-11-02T18:57:59.232Z","isFavorite":false,"isArchived":false,"isTrashed":false,"visibility":"timeline","duration":"0:00:00.00000","exifInfo":{"make":"Apple","model":"iPad mini 2","exifImageWidth":1280<…> ` </details> Also, existing unit tests already cover null response from `getOriginalFilename`. ## Checklist: - [x] I have performed a self-review of my own code - [x] I have no unrelated changes in the PR. - [ ] I have written tests for new code (if applicable) - [x] I have followed naming conventions/patterns in the surrounding code - [ ] All code in `src/repositories/` is pretty basic/simple and does not have any immich specific logic (that belongs in `src/services/`) ## Please describe to which degree, if any, an LLM was used in creating this pull request. Help with dart, compare fix options. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
OVERLORD added the pull-request label 2026-02-05 16:23:44 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: immich-app/immich#17548