amplify-js: Amplify v6, Storage uploadData throws ECONNABORTED error consistently in React Native Testflight Production App
Before opening, please confirm:
- I have searched for duplicate or closed issues and discussions.
- I have read the guide for submitting bug reports.
- I have done my best to include a minimal, self-contained set of instructions for consistently reproducing the issue.
JavaScript Framework
React Native
Amplify APIs
Storage
Amplify Version
v6
Amplify Categories
storage
Backend
Amplify CLI
Environment information
System:
OS: macOS 14.2.1
CPU: (10) arm64 Apple M2 Pro
Memory: 467.20 MB / 16.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 20.7.0 - /opt/homebrew/bin/node
npm: 10.1.0 - /opt/homebrew/bin/npm
Watchman: 2023.09.04.00 - /opt/homebrew/bin/watchman
Browsers:
Safari: 17.2.1
npmPackages:
@aws-amplify/react-native: ^1.0.12 => 1.0.12
@babel/core: ^7.20.0 => 7.23.7
@babel/plugin-proposal-export-namespace-from: ^7.18.9 => 7.18.9
@babel/preset-env: ^7.20.0 => 7.23.8
@babel/runtime: ^7.20.0 => 7.23.8
@expo-google-fonts/m-plus-1p: ^0.2.3 => 0.2.3
@expo-google-fonts/montserrat: ^0.2.3 => 0.2.3
@expo-google-fonts/space-grotesk: ^0.2.2 => 0.2.3
@expo/config-plugins: ~7.2.2 => 7.2.5 (5.0.4)
@expo/metro-config: ^0.10.7 => 0.10.7
@gorhom/bottom-sheet: ^4.4.7 => 4.6.0
@likashefqet/react-native-image-zoom: ^2.1.1 => 2.1.1
@react-native-async-storage/async-storage: ^1.18.2 => 1.21.0
@react-native-clipboard/clipboard: ^1.11.2 => 1.13.2
@react-native-community/cli-platform-ios: ^8.0.2 => 8.0.6 (11.3.7)
@react-native-community/netinfo: ^9.3.10 => 9.5.0
@react-navigation/bottom-tabs: ^6.3.2 => 6.5.11
@react-navigation/native: ^6.0.2 => 6.1.9
@react-navigation/native-stack: ^6.0.2 => 6.9.17
@rnx-kit/metro-config: ^1.3.5 => 1.3.14
@rnx-kit/metro-resolver-symlinks: ^0.1.26 => 0.1.34
@sentry/react-native: 5.10.0 => 5.10.0
@shopify/flash-list: 1.4.3 => 1.4.3
@types/i18n-js: 3.8.2 => 3.8.2
@types/jest: ^29.2.1 => 29.5.11
@types/lodash.filter: ^4.6.9 => 4.6.9
@types/react: ~18.2.14 => 18.2.48
@types/react-test-renderer: ^18.0.0 => 18.0.7
@typescript-eslint/eslint-plugin: ^5.59.0 => 5.62.0
@typescript-eslint/parser: ^5.59.0 => 5.62.0
HelloWorld: 0.0.1
apisauce: 2.1.5 => 2.1.5
aws-amplify: ^6.0.12 => 6.0.12
aws-amplify/adapter-core: undefined ()
aws-amplify/analytics: undefined ()
aws-amplify/analytics/kinesis: undefined ()
aws-amplify/analytics/kinesis-firehose: undefined ()
aws-amplify/analytics/personalize: undefined ()
aws-amplify/analytics/pinpoint: undefined ()
aws-amplify/api: undefined ()
aws-amplify/api/server: undefined ()
aws-amplify/auth: undefined ()
aws-amplify/auth/cognito: undefined ()
aws-amplify/auth/cognito/server: undefined ()
aws-amplify/auth/enable-oauth-listener: undefined ()
aws-amplify/auth/server: undefined ()
aws-amplify/datastore: undefined ()
aws-amplify/in-app-messaging: undefined ()
aws-amplify/in-app-messaging/pinpoint: undefined ()
aws-amplify/push-notifications: undefined ()
aws-amplify/push-notifications/pinpoint: undefined ()
aws-amplify/storage: undefined ()
aws-amplify/storage/s3: undefined ()
aws-amplify/storage/s3/server: undefined ()
aws-amplify/storage/server: undefined ()
aws-amplify/utils: undefined ()
axios: ^1.5.0 => 1.6.5 (0.21.4)
babel-jest: ^29.2.1 => 29.7.0
babel-loader: 8.2.5 => 8.2.5
babel-plugin-root-import: ^6.6.0 => 6.6.0
cheerio: ^1.0.0-rc.12 => 1.0.0-rc.12
core-util-is: 1.0.1
date-fns: ^2.29.2 => 2.30.0
eslint: 8.17.0 => 8.17.0
eslint-config-prettier: 8.5.0 => 8.5.0
eslint-config-standard: 17.0.0 => 17.0.0
eslint-plugin-import: 2.26.0 => 2.26.0
eslint-plugin-n: ^15.0.0 => 15.7.0
eslint-plugin-node: 11.1.0 => 11.1.0
eslint-plugin-promise: 6.0.0 => 6.0.0
eslint-plugin-react: 7.30.0 => 7.30.0
eslint-plugin-react-native: 4.0.0 => 4.0.0
expo: ^49.0.21 => 49.0.21
expo-application: ~5.3.0 => 5.3.1
expo-blur: ~12.4.1 => 12.4.1
expo-build-properties: ~0.8.3 => 0.8.3
expo-clipboard: ~4.3.1 => 4.3.1
expo-config-plugin-ios-share-extension: ^0.0.4 => 0.0.4
expo-constants: ~14.4.2 => 14.4.2
expo-contacts: ~12.2.0 => 12.2.0
expo-dev-client: ~2.4.11 => 2.4.12
expo-device: ~5.4.0 => 5.4.0
expo-file-system: ~15.4.4 => 15.4.5
expo-font: ~11.4.0 => 11.4.0
expo-image: ~1.3.5 => 1.3.5
expo-image-picker: ~14.3.2 => 14.3.2
expo-linear-gradient: ~12.3.0 => 12.3.0
expo-linking: ~5.0.2 => 5.0.2
expo-localization: ~14.3.0 => 14.3.0
expo-media-library: ~15.4.1 => 15.4.1
expo-notifications: ~0.20.1 => 0.20.1
expo-secure-store: ~12.3.1 => 12.3.1
expo-splash-screen: ^0.20.5 => 0.20.5
expo-status-bar: ~1.6.0 => 1.6.0
expo-store-review: ~6.4.0 => 6.4.0
expo-video-thumbnails: ~7.4.0 => 7.4.0
fbjs-scripts: 3.0.1 => 3.0.1
i18n-js: 3.9.2 => 3.9.2
inherits: 2.0.1
isarray: 0.0.1
jest: ^29.2.1 => 29.7.0
jest-circus: 29 => 29.7.0
jest-environment-node: 29 => 29.7.0
jest-expo: ^49.0.0 => 49.0.0
jsdom: ^22.1.0 => 22.1.0 (20.0.3)
jsdom-jscore-rn: ^0.1.8 => 0.1.8
lodash: ^4.17.21 => 4.17.21
lodash.filter: ^4.6.0 => 4.6.0
lottie-react-native: ^5.1.6 => 5.1.6
metro-config: 0.76.8 => 0.76.8
metro-source-map: 0.75.1 => 0.75.1 (0.76.8)
mobx: 6.6.0 => 6.6.0
mobx-react-lite: 3.4.0 => 3.4.0
mobx-state-tree: 5.1.5 => 5.1.5
mocha: ^10.2.0 => 10.2.0
nwmatcher: 1.4.3
patch-package: ^6.4.7 => 6.5.1
path-browserify: 0.0.0
postinstall-prepare: 1.0.1 => 1.0.1
prettier: 2.8.8 => 2.8.8 (3.2.4)
query-string: ^7.0.1 => 7.1.3 (6.10.1)
querystring: 0.2.0
react: 18.2.0 => 18.2.0
react-devtools-core: 4.24.7 => 4.24.7 (4.28.5)
react-dom: 18.2.0 => 18.2.0
react-native: 0.72.6 => 0.72.6
react-native-bootsplash: ^5.0.2 => 5.2.2
react-native-compressor: ^1.8.23 => 1.8.23
react-native-device-info: ^10.12.0 => 10.12.0
react-native-dots-pagination: ^0.3.1 => 0.3.1
react-native-element-dropdown: ^2.9.0 => 2.10.1
react-native-fs: ^2.20.0 => 2.20.0
react-native-gesture-handler: ~2.12.0 => 2.12.1
react-native-get-random-values: ~1.9.0 => 1.9.0
react-native-mmkv: ^2.11.0 => 2.11.0
react-native-pager-view: 6.2.0 => 6.2.0
react-native-reanimated: ~3.3.0 => 3.3.0
react-native-receive-sharing-intent: ^2.0.0 => 2.0.0
react-native-render-html: ^6.3.4 => 6.3.4
react-native-safe-area-context: 4.6.3 => 4.6.3
react-native-screens: ~3.22.0 => 3.22.1
react-native-shimmer-placeholder: ^2.0.9 => 2.0.9
react-native-static-safe-area-insets: ^2.2.0 => 2.2.0
react-native-touchable-scale: ^2.2.0 => 2.2.0
react-native-url-polyfill: ^2.0.0 => 2.0.0
react-native-video: ^6.0.0-beta.4 => 6.0.0-beta.4
react-native-vision-camera: ^3.6.6 => 3.8.0
react-native-volume-manager: ^1.10.0 => 1.10.0
react-native-web: ~0.19.6 => 0.19.10
react-native-webview: 13.2.2 => 13.2.2
react-native-youtube-iframe: ^2.3.0 => 2.3.0
react-test-renderer: 18.2.0 => 18.2.0
reactotron-core-client: ^2.8.10 => 2.8.11 (2.8.10)
reactotron-mst: 3.1.4 => 3.1.4
reactotron-react-js: ^3.3.7 => 3.3.9
reactotron-react-native: 5.0.3 => 5.0.3
regenerator-runtime: ^0.13.4 => 0.13.11 (0.14.1)
sentry-expo: ~7.1.0 => 7.1.1
string_decoder: 0.10.31
ts-jest: 29 => 29.1.1
typescript: ^4.9.4 => 5.3.3
urlmaster: 0.2.15
npmGlobalPackages:
@aws-amplify/cli-internal: 12.10.0
@aws-amplify/cli: 12.10.1
@react-native-community/netinfo: 9.4.1
eas-cli: 7.0.0
expo-cli: 6.3.10
firebase-tools: 11.24.1
n: 9.1.0
node: 20.6.0
npm: 10.3.0
pod-install: 0.1.39
react-native-spinkit: 1.5.1
Describe the bug
Uploading media to S3 via the uploadData api occasionally fails upload and throws ECONNABORTED Network Error. Unable to reproduce exact replication scenario, but it is happening ~30% of the time in production - beta.
Expected behavior
The video should complete the upload successfully, which it does most of the time. Connected to good wifi connection, no bad signal, still receiving this error.
Reproduction steps
- Install amplify v6 CLI via the instructions
- Install expo-image-picker
- Here is the usage:
import * as ImagePicker from "expo-image-picker"
async function openPhotoLibrary() {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync()
if (status !== "granted") {
alert("The app needs access to your local_phone_storage in order for you to use this feature." )
return
}
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
exif: false,
videoQuality: ImagePicker.UIImagePickerControllerQualityType.Low,
// allowsEditing: true,
// aspect: [4, 3],
videoMaxDuration: 29,
quality: 0.1,
})
if (!result.canceled) {
// success
const { uri } = result.assets[0]
const extension = getFileExtension(uri)
const contentType = getContentType(uri)
const mediaUri = await uploadToS3({
filePath: uri,
key: `originalUrl.${extension}`,
options: {
accessLevel: "guest",
contentType: contentType + "/" + extension,
},
})
}
}
- Here is a custom upload function that I have developed for reusability:
import { uploadData, UploadDataInput, getUrl, } from "aws-amplify/storage"
export async function uploadToS3(input: Omit<UploadDataInput, "data"> & { filePath: string }): Promise<string> {
try {
const response = await fetch(input.filePath)
const data = await response.blob()
const uploadDataOutput = uploadData({ key: input.key, data, options: input?.options })
const result = await uploadDataOutput.result
const key = result.key
const urlResponse = await getUrl({
key,
options: {
accessLevel: input.options?.accessLevel,
},
})
const fullUrl = urlResponse.url.toString()
return fullUrl.substring(0, fullUrl.indexOf("?"))
} catch (error) {
reportError({ error, method: "uploadToS3" }). // This is where the error is caught and displayed
return ""
}
}
Code Snippet
Most code is provided above in the reproduction steps.
Log output
Occuring in production copy of app in testflight Beta, so can’t get logs. Refer to Sentry screenshot below.
aws-exports.js
Here is my aws-exports.js: /* eslint-disable */ // WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.
const awsmobile = {
"aws_project_region": "us-east-1",
"aws_appsync_graphqlEndpoint": "https://5tvnvesrgjfjdclupp5rzrh5gi.appsync-api.us-east-1.amazonaws.com/graphql",
"aws_appsync_region": "us-east-1",
"aws_appsync_authenticationType": "API_KEY",
"aws_appsync_apiKey": *REDACTED*,
"aws_cognito_identity_pool_id": *REDACTED*,
"aws_cognito_region": "us-east-1",
"aws_user_pools_id": "us-east-1_eDAImGHL9",
"aws_user_pools_web_client_id": *REDACTED*,
"oauth": {},
"aws_cognito_username_attributes": [],
"aws_cognito_social_providers": [],
"aws_cognito_signup_attributes": [
"GIVEN_NAME",
"FAMILY_NAME",
"BIRTHDATE",
"PHONE_NUMBER"
],
"aws_cognito_mfa_configuration": "OFF",
"aws_cognito_mfa_types": [],
"aws_cognito_password_protection_settings": {
"passwordPolicyMinLength": 8,
"passwordPolicyCharacters": [
"REQUIRES_LOWERCASE",
"REQUIRES_UPPERCASE",
"REQUIRES_NUMBERS",
"REQUIRES_SYMBOLS"
]
},
"aws_cognito_verification_mechanisms": [
"PHONE_NUMBER"
],
"aws_user_files_s3_bucket": "reelfeelmedia",
"aws_user_files_s3_bucket_region": "us-east-1"
};
export default awsmobile;
Manual configuration
Here is my amplifyconfiguration.json:
{
"aws_project_region": "us-east-1",
"aws_appsync_graphqlEndpoint": "https://5tvnvesrgjfjdclupp5rzrh5gi.appsync-api.us-east-1.amazonaws.com/graphql",
"aws_appsync_region": "us-east-1",
"aws_appsync_authenticationType": "API_KEY",
"aws_appsync_apiKey": *REDACTED*,
"aws_cognito_identity_pool_id": *REDACTED*,
"aws_cognito_region": "us-east-1",
"aws_user_pools_id": "us-east-1_eDAImGHL9",
"aws_user_pools_web_client_id": " *REDACTED*,
"oauth": {},
"aws_cognito_username_attributes": [],
"aws_cognito_social_providers": [],
"aws_cognito_signup_attributes": [
"GIVEN_NAME",
"FAMILY_NAME",
"BIRTHDATE",
"PHONE_NUMBER"
],
"aws_cognito_mfa_configuration": "OFF",
"aws_cognito_mfa_types": [],
"aws_cognito_password_protection_settings": {
"passwordPolicyMinLength": 8,
"passwordPolicyCharacters": [
"REQUIRES_LOWERCASE",
"REQUIRES_UPPERCASE",
"REQUIRES_NUMBERS",
"REQUIRES_SYMBOLS"
]
},
"aws_cognito_verification_mechanisms": [
"PHONE_NUMBER"
],
"aws_user_files_s3_bucket": *REDACTED*,
"aws_user_files_s3_bucket_region": "us-east-1"
}
Additional configuration
Here is my configuration in App.tsx:
import { Amplify } from "aws-amplify"
import amplifyconfig from "./app/amplifyconfiguration.json"
const libraryOptions = {
Storage: {
S3: {
prefixResolver: async ({
accessLevel,
targetIdentityId,
}: {
accessLevel: any
targetIdentityId?: string
}) => {
if (accessLevel === "guest") {
return "public/"
} else if (accessLevel === "protected") {
return `protected/${targetIdentityId}/`
} else {
return `private/${targetIdentityId}/`
}
},
},
},
}
Amplify.configure(amplifyconfig, libraryOptions)
Mobile Device
iPhone 13 Pro (Physical)
Mobile Operating System
iOS 17.2.1
Mobile Browser
Safari
Mobile Browser Version
Unsure
Additional information and screenshots
Sentry Error with a little more error details:
iOS error alert:
About this issue
- Original URL
- State: closed
- Created 5 months ago
- Comments: 23 (10 by maintainers)
@cshfang @cwomack Thanks a bunch for digging deep. I’m okay with closing this issue, please keep me updated on any updates you guys do make in this area. Because I’m wading in some unknown waters here, how could I inspect the actual network logs? How is this different than what I’ve already done?
I did open the above issues with
Expoand theReact Nativeteam to hopefully make progress in that area.@cwomack @nadetastic Okay, I finally spent time hunting down the exact source (line of code) that is triggering this error in production. It’s happening at least 5 times a day among my test users and it’s been driving me up the wall.
I put
try-catchblocks around every line in myuploadToS3function. I also had myreportCrashfunction get the network state during the time, and the aws authentication state. Both appear to be good, although this does happen more often with poor signal.Here is what I can tell you so far:
fetchAuthSession({forceRefresh: true})before calling this does not solve the problem.This &$*@ has plagued us like a menace for the last 3 months and I will Venmo whoever solves this 50$ for beer.
If there is anything I can do, like you guys provide me with a patch file that has better logging for you using
patch-packageon theuploadData().resultfunction, I will gladly do it. There is something in that package throwing this error.If you send me the background code of this uploadData function and I can use it in place of the uploadData function and break it down with
try-catcheseven further. I will literally do anything to solve this lol…Here are screenshots of the native alert I have displayed for the user:
@cwomack The place where this error comes the most often is this function:
The only method in here that is not an amplify related method is the
sendResponseNotification. The only other methods areclient.graphqland the amplifyuploadfunction. This method uses fetch:I know it doesn’t get to this point though because the other user does not get the notification, and it is the uploadToS3 that actually fails. Do any of these amplify functions use
axiosunder the hood? I can’t replicate this issue in the development environment. It only happens every now and then in the production environment.