Provide environment information
el_badi_dev@ELs-MacBook-Air birinia % npx envinfo --system --binaries --browsers --npmPackages "typescript,uploadthing,@uploadthing/react,@uploadthing/solid"
Need to install the following packages:
envinfo@7.21.0
Ok to proceed? (y) y
System:
OS: macOS 26.2
CPU: (8) arm64 Apple M1
Memory: 219.23 MB / 8.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 24.4.1 - /Users/el_badi_dev/.nvm/versions/node/v24.4.1/bin/node
npm: 11.4.2 - /Users/el_badi_dev/.nvm/versions/node/v24.4.1/bin/npm
pnpm: 10.12.1 - /opt/homebrew/bin/pnpm
bun: 1.0.35 - /opt/homebrew/bin/bun
Watchman: 2025.09.15.00 - /opt/homebrew/bin/watchman
Browsers:
Chrome: 146.0.7680.81
Edge: 146.0.3856.62
Safari: 26.2
npmPackages:
typescript: ~5.9.2 => 5.9.3
uploadthing: ^7.7.3 => 7.7.3
el_badi_dev@ELs-MacBook-Air birinia %
Describe the bug
🐛 [expo] Network request failed on Expo SDK 55 / React Native 0.83 — onBeforeUploadBegin never called
Description
After upgrading from Expo SDK 53 (RN 0.79) to Expo SDK 55 (RN 0.83), all uploads fail immediately with a Network request failed error. The crash happens before onBeforeUploadBegin is ever called, meaning the failure occurs during the initial presigned URL request — before the upload pipeline even starts.
The same codebase, same uploadthing versions, same backend — works perfectly on SDK 53.
Error
ERROR [Error: Uncaught (in promise, id: 0) TypeError: Network request failed]
Code: fetch.umd.js
565 | xhr.onerror = function() {
566 | setTimeout(function() {
> 567 | reject(new TypeError('Network request failed'));
| ^
568 | }, 0);
569 | };
Call Stack:
setTimeout$argument_0 (node_modules/whatwg-fetch/dist/fetch.umd.js:567:31)
Reproduction
- Working project on Expo SDK 53 / RN 0.79 with
@uploadthing/expo
- Upgrade to Expo SDK 55 / RN 0.83 (New Architecture mandatory, no opt-out)
- Trigger
openImagePicker
onBeforeUploadBegin is never called — crash happens before it
Environment
| |
SDK 53 (working) |
SDK 55 (broken) |
| expo |
^53.0.20 |
^55.0.6 |
| react-native |
0.79.5 |
0.83.2 |
| @uploadthing/expo |
7.2.5 |
7.2.6 |
| uploadthing |
7.7.3 |
7.7.4 |
| expo-image-picker |
~16.1.4 |
~55.0.12 |
| Architecture |
Old (opt-in new) |
New Architecture only |
Backend: Convex HTTP action (https://xxx.convex.site)
Platform: Android (physical device), both WiFi and mobile data
What was tried
- ✅
@bacons/text-decoder polyfill
- ✅
expo/fetch polyfill via polyfillGlobal
- ✅ Blocking
whatwg-fetch in metro resolveRequest
- ✅ Blocking specifically when origin is
Libraries/Network/fetch.js
- ✅ Downgrading
expo-image-picker and expo-document-picker to SDK 53 versions
- ✅ Simplifying
onBeforeUploadBegin to just pass the file through unchanged
- ✅ Removing
react-native-compressor entirely
- ✅ Verified
EXPO_PUBLIC_SERVER_URL is correct (https://xxx.convex.site)
Key finding
react-native/Libraries/Network/fetch.js does require('whatwg-fetch') in both RN 0.79 and RN 0.83 — so this is not a new change. The whatwg-fetch polyfill was present and working in SDK 53, but causes Network request failed in SDK 55 / RN 0.83 New Architecture (Bridgeless mode).
// node_modules/react-native/Libraries/Network/fetch.js
'use strict';
require('whatwg-fetch'); // <-- present in both RN 0.79 and RN 0.83
export const fetch = global.fetch;
...
This suggests the issue is specifically with how Bridgeless / New Architecture in RN 0.83 handles XHR-based requests from whatwg-fetch when uploadthing makes the initial presigned URL fetch.
Question
Is @uploadthing/expo tested against Expo SDK 55 / RN 0.83 with New Architecture (Bridgeless)? Is there a known workaround or fix in progress?
Link to reproduction
https//:google.com
To reproduce
Steps to Reproduce
- Set up a React Native / Expo project using Expo SDK 55 (React Native 0.83, New Architecture mandatory)
- Install uploadthing packages:
npm install uploadthing @uploadthing/expo expo-image-picker expo-document-picker
- Set up a Convex HTTP action as the uploadthing backend and set
EXPO_PUBLIC_SERVER_URL=https://xxx.convex.site
- Generate helpers in
uploadthing.ts:
import { generateReactNativeHelpers } from "@uploadthing/expo";
export const { useImageUploader } = generateReactNativeHelpers({
url: process.env.EXPO_PUBLIC_SERVER_URL,
});
- Use
useImageUploader in a component and call openImagePicker
- Add a
console.log inside onBeforeUploadBegin to verify it is called
- Run on a physical Android device and trigger the image picker
- Expected:
onBeforeUploadBegin is called, upload proceeds
- Actual: App crashes with
TypeError: Network request failed from whatwg-fetch/dist/fetch.umd.js:567 — onBeforeUploadBegin is never reached
Additional information
Additional Context — Uploadthing Implementation Files
uploadthing.ts (client helper setup)
import { generateReactNativeHelpers } from "@uploadthing/expo";
import { OurFileRouter } from "~/convex/uploadthing/index";
export const { useImageUploader, useDocumentUploader } =
generateReactNativeHelpers<OurFileRouter>({
/**
* Your server url.
* @default process.env.EXPO_PUBLIC_SERVER_URL
* @remarks In dev we will also try to use Expo.debuggerHost
*/
url: process.env.EXPO_PUBLIC_SERVER_URL,
});
Note: Backend is a Convex HTTP action, not Expo API routes. EXPO_PUBLIC_SERVER_URL is set to https://xxx.convex.site.
photo-picker.tsx (upload component)
import { useAuthToken } from "@convex-dev/auth/react";
import { openSettings } from "expo-linking";
import React from "react";
import { Alert } from "react-native";
import { PhotoIcon } from "react-native-heroicons/outline";
import { useImageUploader } from "~/src/lib/uploadthing";
import { Button } from "../ui/button";
import { useTheme } from "~/src/hooks/use-theme";
interface PhotoPickerProps {
onImageSelected: (localUri: string) => string;
onUploadComplete: (imageId: string, uploadedUrl: string) => void;
onUploadError: (imageId: string, error: string) => void;
}
const PhotoPicker = ({
onImageSelected,
onUploadComplete,
onUploadError,
}: PhotoPickerProps) => {
const { theme } = useTheme();
const token = useAuthToken();
let currentImageId: string | null = null;
const { openImagePicker } = useImageUploader("imageUploader", {
headers: (): Record<string, string> => {
if (!token) return {};
return { Authorization: `Bearer ${token}` };
},
onClientUploadComplete: async (e) => {
if (e[0].ufsUrl && e[0].customId && currentImageId) {
const urlParts = e[0].ufsUrl.split("/");
urlParts.pop();
const baseUrl = urlParts.join("/");
const imageUrl = `${baseUrl}/${e[0].customId}`;
onUploadComplete(currentImageId, imageUrl);
currentImageId = null;
}
},
// ⚠️ This callback is NEVER reached — crash happens before it
onBeforeUploadBegin: async (files: any[]) => {
const file = files[0];
currentImageId = onImageSelected(file.uri);
return [file];
},
onUploadError: (error) => {
if (currentImageId) {
onUploadError(currentImageId, error.message || "فشل في رفع الصورة");
currentImageId = null;
}
Alert.alert("خطأ في الرفع", error.message || "فشل في رفع الصورة");
},
});
const handlePress = () => {
openImagePicker({
source: "library",
onInsufficientPermissions: () => {
Alert.alert(
"لا توجد صلاحيات",
"تحتاج إلى منح صلاحية للوصول إلى الصور لاستخدام هذه الميزة",
[{ text: "إلغاء" }, { text: "فتح الإعدادات", onPress: openSettings }]
);
},
});
};
return (
<Button
onPress={handlePress}
variant="outline"
className="w-20 h-20 rounded-xl border-2 border-dashed border-gray-300 bg-gray-50"
>
<PhotoIcon size={24} color={theme.foreground} />
</Button>
);
};
export default PhotoPicker;
Key observations
onBeforeUploadBegin is never called — confirmed via console log
- The crash is in
whatwg-fetch/dist/fetch.umd.js at the XHR onerror handler
- This means the failure is in the initial presigned URL request to the Convex backend, before the upload pipeline starts
- The same code works on SDK 53 / RN 0.79 with no changes
👨👧👦 Contributing
Code of Conduct
Provide environment information
Describe the bug
🐛 [expo] Network request failed on Expo SDK 55 / React Native 0.83 —
onBeforeUploadBeginnever calledDescription
After upgrading from Expo SDK 53 (RN 0.79) to Expo SDK 55 (RN 0.83), all uploads fail immediately with a
Network request failederror. The crash happens beforeonBeforeUploadBeginis ever called, meaning the failure occurs during the initial presigned URL request — before the upload pipeline even starts.The same codebase, same uploadthing versions, same backend — works perfectly on SDK 53.
Error
Reproduction
@uploadthing/expoopenImagePickeronBeforeUploadBeginis never called — crash happens before itEnvironment
Backend: Convex HTTP action (
https://xxx.convex.site)Platform: Android (physical device), both WiFi and mobile data
What was tried
@bacons/text-decoderpolyfillexpo/fetchpolyfill viapolyfillGlobalwhatwg-fetchin metroresolveRequestLibraries/Network/fetch.jsexpo-image-pickerandexpo-document-pickerto SDK 53 versionsonBeforeUploadBeginto just pass the file through unchangedreact-native-compressorentirelyEXPO_PUBLIC_SERVER_URLis correct (https://xxx.convex.site)Key finding
react-native/Libraries/Network/fetch.jsdoesrequire('whatwg-fetch')in both RN 0.79 and RN 0.83 — so this is not a new change. Thewhatwg-fetchpolyfill was present and working in SDK 53, but causesNetwork request failedin SDK 55 / RN 0.83 New Architecture (Bridgeless mode).This suggests the issue is specifically with how Bridgeless / New Architecture in RN 0.83 handles XHR-based requests from
whatwg-fetchwhen uploadthing makes the initial presigned URL fetch.Question
Is
@uploadthing/expotested against Expo SDK 55 / RN 0.83 with New Architecture (Bridgeless)? Is there a known workaround or fix in progress?Link to reproduction
https//:google.com
To reproduce
Steps to Reproduce
EXPO_PUBLIC_SERVER_URL=https://xxx.convex.siteuploadthing.ts:useImageUploaderin a component and callopenImagePickerconsole.loginsideonBeforeUploadBeginto verify it is calledonBeforeUploadBeginis called, upload proceedsTypeError: Network request failedfromwhatwg-fetch/dist/fetch.umd.js:567—onBeforeUploadBeginis never reachedAdditional information
Additional Context — Uploadthing Implementation Files
uploadthing.ts(client helper setup)photo-picker.tsx(upload component)Key observations
onBeforeUploadBeginis never called — confirmed via console logwhatwg-fetch/dist/fetch.umd.jsat the XHRonerrorhandler👨👧👦 Contributing
Code of Conduct