Short videos save to the gallery just fine, but the long ones never do. The error says there isn't enough space, and the phone has tens of gigabytes free. I came across a report like this in a public Expo issue, and it took me straight back to an afternoon of my own.
As an indie developer who has written image-saving code many times, I once spent half a day staring at the free-space readout because the error message told me to. That is why this issue caught my attention.
What I'd like to say first is that "not enough space" doesn't always mean storage. Below I summarize the report, confirm the boundary number by running it myself, and leave a few lines you can add to check size before saving.
What is reported
In expo/expo issue #47767, createAssetAsync from expo-media-library fails on Android for large files. The environment listed is expo ~55.0.27, expo-media-library ~55.0.18 and react-native 0.83.6. iOS is said to be unaffected.
The error is "Unable to copy file into external storage.", with "Not enough space" attached as the cause. The reporter generated dummy mp4 files in the app cache and got these results:
| File size | Result |
|---|---|
| 1.00 GB | Saved |
| 2.10 GB | Saved |
| 2.20 GB | Failed |
| 4.00 GB | Failed |
The reporter says free storage was sufficient, so the boundary lives in the size of the file, not in the device. As of my check on 2026-10-22 the issue was still open, and a maintainer had confirmed the root cause on main.
Where the boundary number comes from
The reporter's reading is that the save routine calls FileChannel.transferTo once and never loops. On Linux, a single sendfile call moves at most 2,147,479,552 bytes. If one call can't finish the job, the remainder is treated as a failed write, the half-created entry is deleted, and the message says there isn't enough space.
I can't reproduce Android's internals here. What I could check is whether the same cap exists in the same underlying mechanism (sendfile) on Linux. I created a 2.2GB (2,306,867,200-byte) sparse file and called it once:
import os
size = os.path.getsize("src") # 2,306,867,200 bytes
s = os.open("src", os.O_RDONLY)
d = os.open("dst1", os.O_WRONLY | os.O_CREAT)
n = os.sendfile(d, s, 0, size) # one call only
print(n, size - n)
# 2147479552 159387648Exactly 2,147,479,552 bytes were moved, and 159,387,648 bytes were left over — 4,095 bytes short of the Int32 maximum (2,147,483,647). That puts the boundary in the same place as the issue's "2.10GB passes, 2.20GB doesn't".
Then I changed it to repeat until nothing remained:
d = os.open("dst2", os.O_WRONLY | os.O_CREAT)
off = 0
while off < size:
n = os.sendfile(d, s, off, size - off)
if n == 0: # no progress: treat as a real failure
raise RuntimeError("no progress")
off += n
print(off == size) # True (finished in two calls)Two calls moved all 2.2GB. The fix suggested in the issue follows the same idea: don't assume one call carries everything; advance by what was moved, and treat zero progress as the only real failure.
What the app can do in the meantime
While the library gets fixed, there are two things you can do. Both just give you more clues.
First, check the size before saving and report it with a different message:
import * as FileSystem from "expo-file-system";
import * as MediaLibrary from "expo-media-library";
// About 2GB, placed a little below the boundary reported in the issue
const SAFE_LIMIT_BYTES = 2_000_000_000;
export async function saveVideoToGallery(uri: string) {
const info = await FileSystem.getInfoAsync(uri);
if (!info.exists) {
throw new Error("The video to save was not found");
}
if (info.size > SAFE_LIMIT_BYTES) {
// Tell the user this is not about free storage
throw new Error(
"This video is too large to save to the gallery. Please save it in shorter pieces."
);
}
return MediaLibrary.createAssetAsync(uri);
}I haven't reproduced the 2GB failure of createAssetAsync on a physical device, so treat this snippet as a sketch of the flow, and the limit as a rule of thumb set just under the reported boundary. Please confirm on a real device with your SDK version.
Second, keep any single recording from growing that large: split long recordings into fixed-length segments and you never get near this boundary.
The order I check things in
When "not enough space" appears, I now look in this order:
- Confirm the failing file's size in bytes
- Try saving a smaller file with similar content
- If small ones pass and only big ones fail, suspect a size boundary rather than storage
Free space comes after that. An error message only points at the cause its author imagined.
If you're stuck on permissions or albums instead, see media-save permission scopes and album design and fixing saves to the device library.
Read the error, then check the number yourself. The next time a save fails, I'd suggest starting by printing the file size in bytes. That is where I start now, too.