Skip to content

Intermittent “Network request failed” error in React Native (fetch & Axios) affecting subset of users despite stable backend #56480

@Ieshan-Sharma

Description

@Ieshan-Sharma

Description

We are facing an intermittent “Network request failed” issue in our React Native (Android) application affecting a small subset of users out of our ~40k+ production user base. The issue occurs randomly across different APIs and is not consistently reproducible. It started recently, even though the app had been stable for the past 2.5 years without such errors. Initially, we were using Axios, but after encountering this issue, we migrated to a custom fetch-based wrapper with retry logic; however, the problem still persists. We have verified that the base URL, headers, and token handling via AsyncStorage are correctly implemented. The error does not seem to be tied to any specific endpoint and often resolves automatically after some time or retries. There are no corresponding backend failures observed, which suggests the issue may be related to network conditions, DNS/SSL behavior, or device-specific factors. We are looking for insights into possible root causes and recommended debugging approaches to reliably identify and resolve this issue.

Steps to reproduce

This issue is not consistently reproducible in our development or testing environments. It has only been observed in the production (release) build on Android, where a subset of users across different regions encounter a “Network request failed” error during API calls. The failure does not seem to be tied to any specific endpoint or user action and occurs randomly. Retrying the same request often succeeds after some time. We have not been able to reproduce this issue on local devices or staging builds, which makes it difficult to isolate exact steps. Based on current observations, the issue may be influenced by device-specific conditions, network variability (such as mobile data vs WiFi), or regional ISP factors.

React Native Version

0.71.8

Affected Platforms

Runtime - Android

Output of npx @react-native-community/cli info

info Fetching system and libraries information...
System:
  OS: macOS 26.3.1
  CPU: (8) arm64 Apple M1
  Memory: 167.25 MB / 8.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 24.5.0
    path: /Users/ieshansharma/.nvm/versions/node/v24.5.0/bin/node
  Yarn:
    version: 1.22.22
    path: /opt/homebrew/opt/yarn/bin/yarn
  npm:
    version: 11.5.1
    path: /Users/ieshansharma/.nvm/versions/node/v24.5.0/bin/npm
  Watchman:
    version: 2025.07.14.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods: Not Found
SDKs:
  iOS SDK: Not Found
  Android SDK:
    API Levels:
      - "23"
      - "28"
      - "29"
      - "30"
      - "31"
      - "33"
      - "34"
      - "35"
      - "36"
    Build Tools:
      - 30.0.2
      - 30.0.3
      - 31.0.0
      - 33.0.0
      - 34.0.0
      - 35.0.0
      - 36.0.0
    System Images:
      - android-31 | Google Play ARM 64 v8a
      - android-33 | Google Play ARM 64 v8a
      - android-34 | Google APIs ARM 64 v8a
      - android-35 | Google APIs ARM 64 v8a
      - android-36 | Google Play ARM 64 v8a
      - android-37.0 | Pre-Release 16 KB Page Size Google Play ARM 64 v8a
    Android NDK: Not Found
IDEs:
  Android Studio: 2025.3 AI-253.29346.138.2531.14876573
  Xcode:
    version: /undefined
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.16
    path: /Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home/bin/javac
  Ruby:
    version: 2.6.10
    path: /usr/bin/ruby
npmPackages:
  "@react-native-community/cli":
    installed: 20.1.0
    wanted: 6.1.0
  react:
    installed: 19.2.3
    wanted: 18.2.0
  react-native:
    installed: 0.84.0
    wanted: 0.71.8
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: true
  newArchEnabled: false

info React Native v0.85.1 is now available (your project is running on v0.84.0).
info Changelog: https://github.com/facebook/react-native/releases/tag/v0.85.1
info Diff: https://react-native-community.github.io/upgrade-helper/?from=0.84.0&to=0.85.1
info For more info, check out "https://reactnative.dev/docs/upgrading?os=macos".

Stacktrace or Logs

Network request failed,

Socket closed,

Connection Reset,

Network Error

MANDATORY Reproducer

Due to the nature of this issue, we are unable to provide a minimal public reproducer. The application is client-owned and cannot be open-sourced, and the issue only occurs intermittently in production (release builds) for a subset of users across different regions. It does not reproduce in debug builds or controlled environments. We have attempted to isolate the issue using a minimal setup with similar networking logic (fetch/axios, headers, token handling), but the problem could not be reproduced outside the production environment. Based on current findings, the issue appears to be influenced by real-world network conditions, device configurations, or ISP-level factors rather than deterministic application logic. We are happy to provide additional logs, request/response details, or test specific hypotheses if suggested.

Screenshots and Videos

{
"name": "mobile-app",
"version": "3.1.8",
"private": true,
"scripts": {
"android": "yarn install && react-native run-android",
"ios": "yarn install && react-native run-ios",
"lint": "eslint .",
"start": "react-native start",
"release": "bash release-build.sh",
"play-store": "bash play-store-build.sh",
"clean-build": "bash clean-build.sh",
"prettier:format": "prettier --write "{,!(node_modules),!(build)/**/}*.{js,jsx}"",
"test": "jest",
"postinstall": "patch-package",
"postversion": "./update-build-gradle-version.sh $(node -p -e "require('./package.json').version")"
},
"dependencies": {
"@babel/eslint-parser": "^7.25.9",
"@logrocket/react-native": "^1.44.0",
"@react-native-async-storage/async-storage": "1.18.2",
"@react-native-community/checkbox": "0.5.15",
"@react-native-community/cli": "6.1.0",
"@react-native-community/cli-platform-android": "6.1.0",
"@react-native-community/cli-platform-ios": "6.1.0",
"@react-native-community/datetimepicker": "7.3.0",
"@react-native-community/netinfo": "^11.4.1",
"@react-native-firebase/app": "18.7.3",
"@react-native-firebase/messaging": "18.7.3",
"@notifee/react-native": "^5.7.0",
"@react-native-firebase/crashlytics": "18.7.3",
"@react-native-firebase/perf": "18.7.3",
"@react-navigation/bottom-tabs": "6.0.9",
"@react-navigation/drawer": "^6.1.8",
"@react-navigation/native": "6.0.6",
"@react-navigation/native-stack": "6.2.5",
"@rneui/base": "^4.0.0-rc.8",
"@rneui/themed": "^4.0.0-rc.8",
"@sparkfabrik/react-native-idfa-aaid": "^1.2.0",
"axios": "^1.14.0",
"deprecated-react-native-prop-types": "4.1.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-react": "^7.37.3",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-native": "^5.0.0",
"jail-monkey": "^2.8.3",
"jsc-android": "250231.0.0",
"lottie-react-native": "6.0.1",
"moment": "2.29.4",
"patch-package": "^7.0.2",
"postinstall-postinstall": "^2.1.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "0.71.8",
"react-native-background-fetch": "4.0.3",
"react-native-biometrics": "3.0.1",
"react-native-camera": "4.2.1",
"react-native-chart-kit": "6.12.0",
"react-native-compressor": "1.8.22",
"react-native-country-picker-modal": "^2.0.0",
"react-native-datepicker": "1.7.2",
"react-native-device-info": "10.7.0",
"react-native-document-picker": "9.0.1",
"react-native-eject": "0.2.0",
"react-native-flash-message": "0.4.1",
"react-native-fs": "2.20.0",
"react-native-geolocation-service": "5.3.1",
"react-native-gesture-handler": "2.11.0",
"react-native-image-crop-picker": "0.40.3",
"react-native-image-crop-tools": "1.6.4",
"react-native-image-marker": "1.1.15",
"react-native-image-picker": "7.1.2",
"react-native-intent-launcher": "^0.2.1",
"react-native-linear-gradient": "^2.8.3",
"react-native-modal-datetime-picker": "17.0.0",
"react-native-month-year-picker": "^1.9.0",
"react-native-otp-textinput": "1.0.1",
"react-native-otp-verify": "1.1.6",
"react-native-permissions": "3.8.4",
"react-native-phone-input": "^1.3.7",
"react-native-phone-number-input": "2.1.0",
"react-native-qrcode-svg": "6.2.0",
"react-native-reanimated": "3.6.1",
"react-native-responsive-screen": "1.4.2",
"react-native-restart": "^0.0.27",
"react-native-safe-area-context": "4.5.3",
"react-native-screens": "3.23.0",
"react-native-signature-capture": "0.4.12",
"react-native-smooth-pincode-input": "1.0.9",
"react-native-splash-screen": "3.3.0",
"react-native-sqlite-storage": "6.0.1",
"react-native-svg": "13.10.0",
"react-native-table-component": "1.2.2",
"react-native-uuid": "2.0.1",
"react-native-vector-icons": "9.2.0",
"react-native-version-check": "3.4.7",
"react-native-vision-camera": "4.0.0",
"react-native-webview": "13.6.3",
"react-redux": "8.0.5",
"redux": "4.2.1",
"redux-thunk": "2.4.2",
"rn-fetch-blob": "^0.12.0",
"victory-native": "36.6.11"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@babel/preset-env": "^7.20.0",
"@babel/runtime": "^7.20.0",
"@react-native-community/eslint-config": "^3.2.0",
"@tsconfig/react-native": "^2.0.2",
"@types/jest": "^29.2.1",
"@types/react": "^18.0.24",
"@types/react-test-renderer": "^18.0.0",
"babel-jest": "^29.2.1",
"eslint": "^9.17.0",
"jest": "^29.2.1",
"metro-react-native-babel-preset": "0.73.9",
"prettier": "^3.4.2",
"react-test-renderer": "18.2.0",
"typescript": "4.8.4"
},
"jest": {
"preset": "react-native"
},
"overrides": {
"node-fetch": "3.2.0"
}
}

import axios from 'axios';
import { BaseUrl } from './config';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { replaceStack } from '../../navigation/navigationHelper';
import apiCalledConstants from './apiCalledConstants';
import LogRocket from '@logrocket/react-native';

export class AxiosConfig {
constructor(headers = {}, isUnauthorized = false, version = 'v1') {
const baseUrl = BaseUrl.getBaseUrl(version);

    this.http = axios.create({
        baseURL: baseUrl,
        withCredentials: true
    });

    this.http.interceptors.request.use(async (config) => {
        const token = await AsyncStorage.getItem('token');

        config.headers = {
            'x-mobile-application': 1,
            'Content-Type': 'application/json',
            ...config.headers,
            ...headers
        };

        if (!isUnauthorized && token) {
            config.headers.Authorization = `Bearer ${token}`;
        }

        return config;
    });

    this.http.interceptors.response.use(
        (response) => [response.data, response.status],
        async (error) => {
            if (error.response) {
                if (error.response.status === 401) {
                    await handleUnauthorized();
                    return Promise.reject('Unauthorized');
                }

                throw error.response.data;
            } else if (error.request) {
                throw error.request;
            } else {
                throw error.message;
            }
        }
    );

    for (const method of ['get', 'post', 'put', 'delete']) {
        this[method] = this.http[method];
    }
}

}

const handleUnauthorized = async () => {
Object.keys(apiCalledConstants).forEach((k) => delete apiCalledConstants[k]);

await AsyncStorage.multiRemove(['isEnabled', 'token', 'formID', 'headerValues', 'userData', 'deviceId']);

replaceStack('Login');

};

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

function isNetworkFailure(err) {
if (!err) return false;

let msg = '';

if (typeof err === 'string') {
    msg = err;
} else if (typeof err?.message === 'string') {
    msg = err.message;
} else if (typeof err?.toString === 'function') {
    msg = err.toString();
}

return typeof msg === 'string' && msg.toLowerCase().includes('network request failed');

}

export const fetchWrapper = async ({
url,
method = 'POST',
data = null,
headers = {},
version,
skipToken = false,
retries = 0,
retryDelayMs = 600
}) => {
try {
const baseUrl = BaseUrl.getBaseUrl(version);
const token = await AsyncStorage.getItem('token');

    let authHeader = {};
    if (!skipToken && token) {
        authHeader = { Authorization: `Bearer ${token}` };
    }

    const finalHeaders = {
        ...authHeader,
        'x-request-id': Date.now().toString(),
        ...headers
    };

    let body = data;
    const isFormData = typeof FormData !== 'undefined' && data instanceof FormData;

    if (isFormData) {
        delete finalHeaders['Content-Type'];
        delete finalHeaders['content-type'];
    } else if (data !== null && typeof data === 'object' && !Array.isArray(data)) {
        body = JSON.stringify(data);
        if (!('Content-Type' in finalHeaders) && !('content-type' in finalHeaders)) {
            finalHeaders['Content-Type'] = 'application/json';
        }
    } else if (!('Content-Type' in finalHeaders) && !('content-type' in finalHeaders)) {
        finalHeaders['Content-Type'] = 'multipart/form-data';
    }

    const doFetch = async () =>
        fetch(baseUrl + url, {
            method,
            headers: finalHeaders,
            body
        });

    let response;
    let attempt = 0;
    while (true) {
        try {
            response = await doFetch();
            break;
        } catch (err) {
            const canRetry = attempt < retries && isNetworkFailure(err);
            if (!canRetry) throw err;
            attempt += 1;
            const backoff = retryDelayMs * attempt;
            await sleep(backoff);
        }
    }

    let resData;
    try {
        resData = await response.json();
    } catch {
        resData = null;
    }

    if (response.status === 401) {
        await handleUnauthorized();
        throw new Error('Unauthorized');
    }

    if (!response.ok) {
        throw new Error(resData?.message || `HTTP ${response.status}`);
    }

    return [resData, response.status];
} catch (error) {
    LogRocket.captureException(error);
    throw error;
}

};

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs: AttentionIssues where the author has responded to feedback.Needs: ReproThis issue could be improved with a clear list of steps to reproduce the issue.Platform: iOSiOS applications.Type: Too Old Version🌐NetworkingRelated to a networking API.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions