Android is fine and the problem does not occur on Expo Go nor development build. It only happens on preview/production build. Problem also seems to have surfaced on SDK 51 as it was working fine before (as tested on SDK 49 & 50).
export async function readAssetFile({ file }: { file: any}) {
const [{ localUri, name, hash, type }] = await Asset.loadAsync(file);
let uri = localUri ?? '';
if (!uri?.startsWith('file://')) {
if (Platform.OS === 'android') {
uri = `${cacheDirectory}ExponentAsset-${hash}.${type}`;
return readAsStringAsync(uri, { encoding: 'base64' });
const imgRequire = require('./assets/test-image.png');
const imgBase64 = await readAssetFile({file: imgRequire});
@NilsBaumgartner1994 tried the solution from the same Android thread before and it resulted in the same error, suspecting it is throwing the exception when its trying to get the read permission. I purposely omitted the App.json from the example since either way you will need to build your own standalone app + publish an update through eas update
. You may copy both the App.js and assets directory to your own project for reproducing the example.
Definition of the expo-asset
config in app.json
also seem to be needed only if you require to load the asset at build time and is constraint to a few types which in our case require to load html files to load into a webview which does not seems to be supported.
Hi @brentvatne can we get the expo team support on this or suggest a temporary workaround this issue?
@roys000 thanks for clarification. Since i am exceriencing the same issue, i just wanted to double check.
My "workaround" currently is to put the base64 or file content into a function...
@brentvatne +1
I followed the repro instructions, and find that the file is not readable even before doing the update.
Prebuild is showing me warnings like
`.html` is not a supported asset type
So I will try this with a supported file type for the asset, and see if I can reproduce the original issue.
OK after debugging, it appears that the file system utils in expo-modules-core
do not include the application support directory when checking for file permissions of files within the app.
@roys000 @NilsBaumgartner1994 the patch below seems to work for me, could you see if this fixes the issue?
diff --git a/node_modules/expo-modules-core/ios/FileSystemUtilities/FileSystemLegacyUtilities.swift b/node_modules/expo-modules-core/ios/FileSystemUtilities/FileSystemLegacyUtilities.swift
index 5481a53..59f04b4 100644
--- a/node_modules/expo-modules-core/ios/FileSystemUtilities/FileSystemLegacyUtilities.swift
+++ b/node_modules/expo-modules-core/ios/FileSystemUtilities/FileSystemLegacyUtilities.swift
@@ -10,17 +10,22 @@ public class FileSystemLegacyUtilities: NSObject, EXInternalModule, EXFileSystem
@objc
public var cachesDirectory: String
+ @objc
+ public var applicationSupportDirectory: String
var isScoped: Bool = false
@objc
- public init(documentDirectory: String, cachesDirectory: String) {
+ public init(documentDirectory: String, cachesDirectory: String, applicationSupportDirectory: String) {
self.documentDirectory = documentDirectory
self.cachesDirectory = cachesDirectory
+ self.applicationSupportDirectory = applicationSupportDirectory
self.isScoped = true
super.init()
ensureDirExists(withPath: self.cachesDirectory)
ensureDirExists(withPath: self.documentDirectory)
+ ensureDirExists(withPath: self.applicationSupportDirectory)
required public override init() {
@@ -30,9 +35,14 @@ public class FileSystemLegacyUtilities: NSObject, EXInternalModule, EXFileSystem
let cachesPaths = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)
self.cachesDirectory = cachesPaths[0]
+ let applicationSupportDirectoryPaths =
+ NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true)
+ self.applicationSupportDirectory = applicationSupportDirectoryPaths[0]
super.init()
ensureDirExists(withPath: self.cachesDirectory)
ensureDirExists(withPath: self.documentDirectory)
+ ensureDirExists(withPath: self.applicationSupportDirectory)
public static func exportedInterfaces() -> [Protocol] {
@@ -84,7 +94,7 @@ public class FileSystemLegacyUtilities: NSObject, EXInternalModule, EXFileSystem
@objc
public func getInternalPathPermissions(_ url: URL) -> EXFileSystemPermissionFlags {
- let scopedDirs: [String] = [cachesDirectory, documentDirectory]
+ let scopedDirs: [String] = [cachesDirectory, documentDirectory, applicationSupportDirectory]
let standardizedPath = url.standardized.path
for scopedDirectory in scopedDirs {
if standardizedPath.hasPrefix(scopedDirectory + "/") || standardizedPath == scopedDirectory {
diff --git a/node_modules/expo-modules-core/ios/Interfaces/FileSystem/EXFileSystemInterface.h b/node_modules/expo-modules-core/ios/Interfaces/FileSystem/EXFileSystemInterface.h
index 629c67e..fca4649 100644
--- a/node_modules/expo-modules-core/ios/Interfaces/FileSystem/EXFileSystemInterface.h
+++ b/node_modules/expo-modules-core/ios/Interfaces/FileSystem/EXFileSystemInterface.h
@@ -13,6 +13,7 @@ typedef NS_OPTIONS(unsigned int, EXFileSystemPermissionFlags) {
@property (nonatomic, readonly) NSString *documentDirectory;
@property (nonatomic, readonly) NSString *cachesDirectory;
+@property (nonatomic, readonly) NSString *applicationSupportDirectory;
// TODO: Move permissionsForURI to EXFileSystemManagerInterface
- (EXFileSystemPermissionFlags)permissionsForURI:(NSURL *)uri;
Thank you for filing this issue!
This comment acknowledges we believe this may be a bug and there’s enough information to investigate it.
However, we can’t promise any sort of timeline for resolution. We prioritize issues based on severity, breadth of impact, and alignment with our roadmap. If you’d like to help move it more quickly, you can continue to investigate it more deeply and/or you can open a pull request that fixes the cause.
Hello @douglowder! We're also facing this issue at Backpack. I will try to patch your fix and release a build shortly.
I can consistently recreate this issue in our iOS production app (we have a shared slack too) so I will let you know what happens.
Thank you!
`expo-file-system` internally uses `FileSystemLegacyUtilities.swift`,
which was including the cache and documents directories in its
permission checks, but not the application support directory where the
updates directory is located. This PR adds that directory to the
appropriate files.
# Test Plan
- CI should pass
- Bug repro in #29058 should be fixed
# Checklist
Please check the appropriate items below if they apply to your diff.
This is required for changes to Expo modules.
- [ ] Documentation is up to date to reflect these changes (eg:
https://docs.expo.dev and README.md).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
`expo-file-system` internally uses `FileSystemLegacyUtilities.swift`,
which was including the cache and documents directories in its
permission checks, but not the application support directory where the
updates directory is located. This PR adds that directory to the
appropriate files.
# Test Plan
- CI should pass
- Bug repro in #29058 should be fixed
# Checklist
Please check the appropriate items below if they apply to your diff.
This is required for changes to Expo modules.
- [ ] Documentation is up to date to reflect these changes (eg:
https://docs.expo.dev and README.md).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
I still have the same issue. I use
"react-native": "^0.74.2",
"expo-file-system": "^17.0.1",
"expo": "^51.0.14",
in bare workflow.
I'm still experiencing this issue when trying to load assets (using
Asset.loadAsync
to get the URI) -- error is the same except it it will complain about the asset directory instead of the Application Support directory. Does the asset directory need a similar patch?!
expo 51.0.18 btw
Still facing the same problem.Beste Grüße,Nils BaumgartnerAm 05.07.2024 um 11:55 schrieb Menno Luiten ***@***.***>:
I'm still experiencing this issue when trying to load assets (using Asset.loadAsync to get the URI) -- error is the same except it it will complain about the asset directory instead of the Application Support directory. Does the asset directory need a similar patch?!
expo 51.0.18 btw
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: ***@***.***>
Hello, we are receiving this error in our iOS apps now.
{"code":"ERR_FILE_NOT_WRITABLE"}
when using
const chunk = await FileSystem.readAsStringAsync(filePath, {
encoding: FileSystem.EncodingType.Base64,
length,
position,
This is still working fine android. We had upgraded to the latest versions and it was working fine in both iOS and android before the upgrade.
Current versions:
"expo": "~51.0.20",
"expo-file-system": "~17.0.1",
"expo-dev-client": "~4.0.20"
We still have this issue in our apps, when calling readAsStringAsync
await FileSystem.readAsStringAsync(fileUri, {
encoding: FileSystem.EncodingType.Base64,
Error:
[Error: Calling the 'readAsStringAsync' function has failed
→ Caused by: File '/var/mobile/Media/DCIM/148APPLE/IMG_8518.PNG' is not readable]
FileSystem.getInfoAsync
{"exists": true, "isDirectory": false, "md5": "9c9ee65f9066d43a6cc605cc693289e1", "modificationTime": 1721906185.4745755, "size": 251522, "uri": "file:///var/mobile/Media/DCIM/148APPLE/IMG_8518.PNG"}
Versions:
"expo": "51.0.24",
"expo-dev-client": "4.0.19",
"expo-file-system": "17.0.1"
I am also running into this: development build works fine, production build on iOS (v16.7 or v17.6 on iPad) errors out because an asset isn't readable. I have not tried Android.
import { Asset } from 'expo-asset';
import * as FileSystem from 'expo-file-system';
const asset = Asset.fromModule(require('@/assets/myFile.zip'));
await asset.downloadAsync();
// The following line throws an exception with code ERR_FILE_NOT_READABLE
const zipFileBase64 = await FileSystem.readAsStringAsync(asset.localUri, { encoding: 'base64' });
Note that the error is ERR_FILE_NOT_READABLE, not ERR_FILE_NOT_EXISTS. The asset file is there inside the .ipa file.
The value of asset.localUri
is something like file:///var/containers/Bundle/Application/xxxxxxxx-xxx-xxx-xxx-xxxxxxxxxxxx/myapp.app/assets/assets/myFile.zip
.
I'm using the default metro configuration (in which, I believe, .zip files are supported as assets) and the following:
"expo": "~51.0.22",
"expo-asset": "~10.0.10",
"expo-file-system": "~17.0.1",
"react": "18.2.0",
"react-native": "0.74.3",
Any thoughts or workarounds?
@douglowder thanks for the quick response. I have another update, we dug deeper into our codebase and realised that we were using our version of Audio Recorder which was saving the recording in a **/tmp
folder and this behaviour was broken recently. I am in the process of migrating to expo-av
which seems to have resolved the issue. I consider it resolved for our use case so this issue is no longer a priority for us (expo-notifications
is more important 😄). Thanks again for the quick turnaround!
Just encountered a similar issue with react-native-image-crop-picker
which stores images in the tmp directory as well. The tmp directory path comes from the NSTemporaryDirectory()
call (https://github.com/ivpusic/react-native-image-crop-picker/blob/4d9cb70b02b34333dda44e1c0c85c9c505d6fccf/ios/src/ImageCropPicker.m#L231C30-L231C52) which is an acceptable place to store temporary files https://developer.apple.com/documentation/foundation/1409211-nstemporarydirectory.
Would it make sense to add the temporary directory to FileSystemLegacyUtilities
as well?
@arthuralee I had the same react-native-image-crop-picker
error. #30540 fixed the issue for me. I used the following patch in expo-modules-core since it's still unreleased:
diff --git a/node_modules/expo-modules-core/ios/FileSystemUtilities/FileSystemLegacyUtilities.swift b/node_modules/expo-modules-core/ios/FileSystemUtilities/FileSystemLegacyUtilities.swift
index 0463a6d..ec44d45 100644
--- a/node_modules/expo-modules-core/ios/FileSystemUtilities/FileSystemLegacyUtilities.swift
+++ b/node_modules/expo-modules-core/ios/FileSystemUtilities/FileSystemLegacyUtilities.swift
@@ -110,10 +110,11 @@ public class FileSystemLegacyUtilities: NSObject, EXInternalModule, EXFileSystem
return []
var filePermissions: EXFileSystemPermissionFlags = []
- if FileManager.default.isReadableFile(atPath: url.absoluteString) {
+ // fixed with https://github.com/expo/expo/pull/30540
+ if FileManager.default.isReadableFile(atPath: url.path) {
filePermissions.insert(.read)
- if FileManager.default.isWritableFile(atPath: url.absoluteString) {
+ if FileManager.default.isWritableFile(atPath: url.path) {
filePermissions.insert(.write)
return filePermissions
@brentvatne
I'm still running into the issue on iOS (but works on Android), with the following environment:
iOS: 15.8.3
Expo Client Version: 1017616
package.json
"expo": "51.0.28"
"expo-asset": "~10.0.10"
"expo-file-system": "~17.0.1"
"expo-updates": "~0.25.22"
"react": "18.2.0"
"react-native": "0.74.5"
It happily returns values for these two calls to retrieve info for a file bundled into the project:
const sourceAsset = await Asset.fromModule(require('./somePathAndFile.Ext')).downloadAsync()
const sourceInfo = await FileSystem.getInfoAsync(sourceAsset.localUri, {md5: true, size: true});
But, the copy call fails (first attachment):
await FileSystem.copyAsync({from: sourceInfo.uri, to: someTargetFileLocation})
When I check the development information of the application (second attachment), it is showing ExpoGo SDK of 51.0.0, despite my entry in the package.json. Is there a way to force the client to use an explicit major.minor.patch version?
Hello, sadly I have encountered the original issue as well. Any workarounds?
expo and file-system are both up-to-date
I get:
FileSystem.getInfoAsync(file://uri).exists = true
However:
FileSystem.readAsStringAsync(file://uri) Unable to read data: Error: Calling the 'readAsStringAsync' function has failed → Caused by: 'uri' is not readable