Skip to content

Fix Darwin file requests failing for assets with long titles - #1445

Open
yunusuztr wants to merge 1 commit into
fluttercandies:mainfrom
yunusuztr:fix/bound-export-cache-filename
Open

Fix Darwin file requests failing for assets with long titles#1445
yunusuztr wants to merge 1 commit into
fluttercandies:mainfrom
yunusuztr:fix/bound-export-cache-filename

Conversation

@yunusuztr

Copy link
Copy Markdown

Problem

On iOS, every file-returning API fails with NSCocoaErrorDomain 514
(NSFileWriteInvalidFileNameError) for assets whose title is long. In practice this means
videos imported from social apps: they arrive in Photos with the whole caption as their
title, hashtags and percent-escapes included.

makeAssetOutputPath puts that title straight into a path component:

filename = [NSString stringWithFormat:@"%@_%@%@_%@",
            id, modifiedDate, isOrigin ? @"_o" : @"", filenameBase];

With a caption-sized title the component goes past the 255-byte limit, so the
copyItemAtURL:toURL: in exportAssetToFile fails and the asset is reported to the app as
unavailable. Because loadFile, originFile and getMediaUrl all route through this one
method, they fail together.

From the user's side the symptom is misleading: a video that plays fine in Photos looks
broken in the app.

Evidence

Measured on a real device (iPhone, iOS 26). The app logged the raw platform error:

PlatformException(NSCocoaErrorDomain (514), "BAA443E0-9EA7-4BBE-8D7E-35C43E4B8B5...

The quoted string is the start of the output filename, i.e. the localIdentifier prefix
this method builds.

A discriminating test on the same device: videos recorded by the camera play (their titles
are like IMG_1234.MOV), videos imported from Instagram do not.

Passing an explicit darwinFileType does not help, since that only changes the extension and
not the basename. That was also verified on the device.

Fix

Bound and sanitise the title portion of the cache filename.

The title is decorative in this name: localIdentifier and the modification timestamp
already identify the asset and its current version, so shortening the title cannot introduce
collisions between assets or between versions of the same asset.

  • / and : are replaced, so a separator inside a title cannot escape the cache directory.
  • The title is capped at 40 characters. Even if every character is a 4-byte code point that
    is 160 bytes, which leaves room for the identifier, the timestamp and the extension inside
    the 255-byte limit. rangeOfComposedCharacterSequencesForRange: keeps the cut off a
    combining sequence, so the result stays valid UTF-8.

Short titles are untouched, so cache entries for ordinary camera assets keep their current
names and stay valid.

Reproduction

  1. Save a video to Photos from an app that sets a long title (an Instagram download works).
  2. Call AssetEntity.loadFile(), originFile or getMediaUrl() on it.
  3. Before this change the call fails with NSCocoaErrorDomain 514; after it the file is
    returned.

Verification status

The failure and its cause were verified on a real device, as described above. The author has
no Mac, so the patched build is verified only through CI compilation and on-device testing of
the resulting TestFlight build; I will follow up here with the device result. Flagging this
explicitly rather than implying a verification I did not do.

@yunusuztr

Copy link
Copy Markdown
Author

Follow-up on the verification status noted in the description: the patched build has now been tested on the device.

Same iPhone, same two imported videos that previously failed, TestFlight build compiled from this patch:

  • Both videos now play in the app.
  • The app's on-device log, which records the raw platform error, no longer contains a single failure entry for those assets. Before the patch the same session produced six NSCocoaErrorDomain 514 entries for them; after it, zero.

The patch also compiled cleanly in CI against the current Xcode toolchain.

One note in case it is useful for review: an earlier attempt to work around this from the Dart side by passing darwinFileType: PMDarwinAVFileType.mp4 was also tested on the device and did not help, which is consistent with the diagnosis above. It only changes the target extension, while the failure comes from the basename built out of the title.

`makeAssetOutputPath` puts the asset title (or the resource's original
filename) straight into a path component. That string is not under the app's
control: assets imported from social apps carry the whole caption as their
title, hashtags and percent-escapes included.

Measured on a real device (iPhone, iOS 26, 2026-08-26): such videos produce a
path component past the 255-byte limit, and the `copyItemAtURL:toURL:` call in
`exportAssetToFile` fails with NSCocoaErrorDomain 514
(NSFileWriteInvalidFileNameError). Videos that play fine in Photos are reported
to the app as unavailable. Because every file-returning API routes through this
method, the same failure hits `loadFile`, `originFile` and `getMediaUrl`.

The title is decorative in this name: `localIdentifier` plus the modification
timestamp already identify the asset and its current version uniquely, so
shortening the title cannot introduce collisions.

This change:
  * replaces `/` and `:` in the title portion, so a separator inside a title
    cannot escape the cache directory;
  * caps the title portion at 40 characters. Even if every character is a
    4-byte code point that is 160 bytes, which leaves room for the ~65-byte
    identifier/timestamp prefix and the extension inside the 255-byte limit.
    `rangeOfComposedCharacterSequencesForRange:` keeps the cut off a combining
    sequence so the result stays valid UTF-8.

Short titles are unaffected, so existing cache entries for ordinary camera
assets keep their names.
@yunusuztr
yunusuztr force-pushed the fix/bound-export-cache-filename branch from 5a7b770 to ee936eb Compare August 29, 2026 17:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant