Vydia / react-native-background-upload

Upload files in your React Native app even while it's backgrounded. Supports Android and iOS, including camera roll assets.
MIT License
735 stars 336 forks source link

Unknown Error #85

Open L-Yeiser opened 6 years ago

L-Yeiser commented 6 years ago

Occasionally uploads fail with Unknown Error. We are using the library with a forked version react-native-image-crop-picker which allows downloading assets from the cloud. The error occurs intermittently and seems to be an iOs only issue. Attempting the upload again with a the same media often works. We have been unable to find consistent reproduction steps. Is there a way to get a better error so we know where to start with this issue? Has anyone else come across the same thing?

L-Yeiser commented 6 years ago

We were able to match a failed upload with an the corresponding s3 Post. The Unknown Error is triggered by a 400 incomplete body. It would be awesome if s3 error were passed to the upload Event Listeners. Also any ideas as to why we are getting this error would be amazing.

TSMMark commented 6 years ago

Hi @L-Yeiser thanks for the report. If you're unable to reproduce consistently in your environment you could try in a fork of ReactNativeBackgroundUploadExample for a minimal environment

L-Yeiser commented 6 years ago

Unfortunately the failure is intermittent. I have added a substantial amount of error logging and will be pushing a release out in the next few days. Will post more when we have that information.

L-Yeiser commented 6 years ago

@TSMMark we are still unable to reproduce issue but we are seeing it frequently. Looking through our sentry breadcrumbs it appears to occur after an Lost connection to background transfer service error. Any idea why I would be seeing Lost connection errors when connected to high speed wifi?

TSMMark commented 6 years ago

Sorry, I'm not sure. Maybe #83 will help you with handling offline though

L-Yeiser commented 6 years ago

Is there a thread limit? We often spawn 10+ uploads at once.

superandrew213 commented 6 years ago

I'm seeing the same issue on iOS when running multiple uploads at the same time.

TSMMark commented 6 years ago

@superandrew213 @L-Yeiser I see... Are you able to reproduce the issue consistently in a minimal environment? Such as a fork of ReactNativeBackgroundUploadExample? Please post / link

superandrew213 commented 6 years ago

Will look into it. I think it's a timeout issue.

superandrew213 commented 6 years ago

@L-Yeiser do you get the completed event to fire?

Upload.addListener('completed', uploadId, (data) => {
   console.log(data)
})

I'm getting this response from s3

Your socket connection to the server was not read from or written to within the timeout period. Idle connections will be closed.

I'm guessing since we are uploading multiple files some files get blocked and timeout.

For me the error event doesn't fire at all when this happens.

L-Yeiser commented 6 years ago

@superandrew213 the completed event fires when the upload completes but not when I get unknown error - in those cases the error event does fire. Where are you capturing that s3 error? We have only seen a 400 incomplete body error. We have carved out some time this week to dig into it.

superandrew213 commented 6 years ago

@L-Yeiser for me the completed event fires with responseCode 400 and responseBody has timeout error.

At the moment, I am just listening for this error and restart the upload. On the second try it always succeeds.

reidab commented 6 years ago

Looking into this a little more with @L-Yeiser today, we found that the error is coming back from the NSURLSessionDataTask and being caught here. It's an NSURLErrorUnknown error, which doesn't really offer any insight. At the time the error is caught, the HTTP response is nil.

L-Yeiser commented 6 years ago

@superandrew213 retrying the upload does not work in our case. ATM we are batching uploads in groups of three and retrying any failed upload five times before failing the process. These safe guards have lessened the impact of the bug but we are still seeing it.

L-Yeiser commented 6 years ago

The NSURLErrorUnknown is always proceeded by a NSURLErrorBackgroundSessionWasDisconnected error thrown from the same line.

L-Yeiser commented 6 years ago

@TSMMark I was able to reproduce this issue consistently on an iphone 7 from the example app posted above when attempting to upload large video. In the example app we just switched media to multitype in the react-native-image-picker config. This issue may be related to https://github.com/Vydia/react-native-background-upload/issues/53 which was closed without a solution.

L-Yeiser commented 6 years ago

@TSMMark wondering if it would work to use NSURLSessionUploadTask instead of NSURLSessionDataTask? Counter to this line the docs make it sound like NSURLSessionUploadTask is the correct class to use when uploading in the background. This PR is also using NSURLSessionUploadTask. I admittedly have limited understanding of iOs development, but this error is our number one issue.

StevePotter commented 6 years ago

@L-Yeiser I'd be happy to make a PR that uses a different API for iOS uploading. But it will take me some time. Will you test it for me once I do it?

L-Yeiser commented 6 years ago

@StevePotter would be more then happy to test it out.

JustgeekDE commented 6 years ago

We are also seeing the Unknown Error and lost connection to background transfer service fairly often in our app. (See also #93 and #92 ) In our case it only happens when the app is send to the background and we try to trigger the data upload in the handleAppStateChange function.

As far as I can tell these factors cause the error to appear more often:

I'm guessing that the upload process gets killed by the os to free up memory / processing power. Like @L-Yeiser mentioned it seems like the NSURLSessionUploadTask is meant for this scenario, but uploadTaskWithRequest should already return that type.

Changing NSURLSessionDataTask *uploadTask; to NSURLSessionUploadTask *uploadTask; doesn't seem to have any effect. (Kind of expected)

StevePotter commented 6 years ago

@L-Yeiser I checked in the code and we are using the important calls to create the NSUrlSessionUploadTask. We are just casting it as the base type for some reason. I'll fix that, but that's not going to change anything.

For non-multipart, we use https://developer.apple.com/documentation/foundation/nsurlsession/1411550-uploadtaskwithrequest?language=objc

There is another version of that function that also takes in a completion handler. That could avoid having to rely on the session's handler: https://github.com/Vydia/react-native-background-upload/blob/master/ios/VydiaRNFileUploader.m#L282

Maybe, just maybe, that would help. But I'm basically just shifting around APIs and doubt that'll actually fix anything. I might give it a shot but don't hold your breath. If you want to go for it, I'd be happy to give a few tips, or even a phone call.

L-Yeiser commented 6 years ago

@StevePotter thanks so much for looking into it. I can look into it a bit in the coming months

BaderSerhan commented 5 years ago

Hi guys, I am facing the same issue consistently throughout my app. If I upload relatively large videos or multiple videos at once and so on... Did you manage to find a solution and make it work? Any help would be appreciated!

L-Yeiser commented 5 years ago

We never did. We switched libraries and are forcing uploads when the app is active for the time being. We have some tech time scheduled to circle back to the issue in the next month.

BaderSerhan commented 5 years ago

Thank you for taking the time to reply to me, I appreciate it. I think I figured out a way to allow multiple video uploads at the same time (it is only allowing 2 and failing on the 3rd). I am testing it and trying to fix other issues before I submit a PR.

L-Yeiser commented 5 years ago

That would be amazing. Let me know if you want me to test a fork.

jonathangreco commented 5 years ago

@L-Yeiser

We never did. We switched libraries and are forcing uploads when the app is active for the time being. We have some tech time scheduled to circle back to the issue in the next month.

Wich library do you use ? Is this library can handle large file upload ?

Thanks for your reply

zavin27 commented 5 years ago

any progress on the fix? I'm still getting "unknown error" when trying to upload a video that has more than 100mb on iOS.

TSMMark commented 5 years ago

What do you guys think of what @cristianoccazinsp pointed out in #155?

mzdon commented 5 years ago

I was having some issues on iOS and ended up forking the repo and messing around with it to try and get it working... take whatever I'm about to say with a big grain of salt because this is literally the first time I've ever looked at Objective-C code and I'm a few months into my first React Native project.

The uploader uses uploadTaskWithRequest:fromFile to create the session task. Background sessions require the task to be created from a file URI, so this seems right. However, it never worked for me and I would find that every upload resulted in an empty 0 byte file uploaded to S3. I found some App Sandbox errors in my device console.

Sandbox: app_name(930) deny(1) file-issue-extension target:/private/var/mobile/Media/DCIM/102APPLE/IMG_2942.JPG class:com.apple.nsurlsessiond.readonly

Sandbox: nsurlsessiond(91) deny(1) file-read-metadata /private/var/mobile/Media/DCIM/102APPLE/IMG_2942.JPG

So what I'm guessing is happening is that the file upload is just failing to read the media contents and send them along to S3. It's just weird the upload still happened and everything appeared to work on the client side.

What I had to do was make a copy of the file I was uploading to a temporary location within my app sandbox, create the upload task with the temporary file path, and then clean up that temp file after the upload was complete. After that change I've been able to upload fairly large video files to S3 in the background without any issues.

I tried reading the file contents and using uploadTaskWithRequest:fromData but that failed every time the app would go into the background with Lost connection to background transfer service. I also tried always using uploadTaskWithStreamedRequest and implementing the needNewBodyStream delegate, but that would also fail with Lost connection to background transfer service every time the app went into the background.

I also found that attempting to use the multipart upload mode did seem to work as far as uploading a file goes, but S3 didn't understand what I was uploading and I'd find my files were stored in a raw data file format where you'd be able to open it, view the headers, see the content boundary strings, and a bunch of binary data. So that didn't really work out for me.

Hope this helps. I was at this for days...

babitakapoor112 commented 3 years ago

I am getting following error while uploading a video in android {error: "Upload error", id: "938b77cb-0e6f-4191-87a0-7e50fab0d1b4"} It is successfully uploading a image file but not video file and below is my code example const userToken = await AsyncStorage.getItem('accessToken'); const tokenType = await AsyncStorage.getItem('tokenType'); const options = { url: CONST.API_URL+'demoUpload', path: path.replace('file://', ''), method: 'POST', field: 'upload_file', type: 'multipart', maxRetries: 2, // set retry count (Android only). Default 2 headers: { Accept: 'application/json', 'Content-Type': 'multipart/form-data', Authorization: tokenType + ' ' + userToken, }, // Below are options only supported on Android notification: { enabled: true }, useUtf8Charset: true }

    Upload.startUpload(options).then((uploadId) => {
      console.log('Upload started')
      Upload.addListener('progress', uploadId, (data) => {
        console.log(`Progress: ${data.progress}%`)
      })
      Upload.addListener('error', uploadId, (data) => {
      alert(data.error);
        console.log(`Error: ${data.error}%`)
        console.log(data);
         this.setState({isLoading: false});
      })
      Upload.addListener('cancelled', uploadId, (data) => {
        console.log(`Cancelled!`)
      })
      Upload.addListener('completed', uploadId, async (data) => {
        // data includes responseCode: number and responseBody: Object
        console.log('Completed!');
        let responseJson = JSON.parse(data.responseBody);
        console.log('response saved');
        console.log(responseJson);
      })
    }).catch((err) => {
    console.log(err);
      console.log('Upload error!', err)
    })