[GH-ISSUE #8420] Session is set to null on network reconnect (mobile) #28408

Closed
opened 2026-04-17 19:51:59 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @florian-deroo on GitHub (Mar 5, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/8420

Originally assigned to: @bytaesu on GitHub.

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Authenticate successfully on a mobile device (React Native / Expo)
  2. Turn off internet (WiFi + cellular)
  3. Navigate around the app — session is intact, useSession still returns data ✓
  4. Turn internet back on
  5. → useSession immediately sets data: null → app redirects to login screen ✗

Bonus step to confirm the token is still valid:
6. Force-quit and reopen the app → session is automatically restored ✓
(proves the token in SecureStore was never invalidated server-side)

Current vs. Expected behavior

Current behavior:

When a mobile device loses internet and reconnects, useSession sets data: null and triggers a logout — even though the session token in SecureStore is still perfectly valid.

Expected behavior:

A transient network failure during session revalidation should preserve the previously cached session data. Only an explicit 401 Unauthorized response from the server should invalidate the session and trigger a logout.

What version of Better Auth are you using?

1.4.16

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 25.3.0: Wed Jan 28 20:49:24 PST 2026; root:xnu-12377.81.4~5/RELEASE_ARM64_T8132",
    "release": "25.3.0",
    "cpuCount": 10,
    "cpuModel": "Apple M4",
    "totalMemory": "24.00 GB",
    "freeMemory": "0.42 GB"
  },
  "node": {
    "version": "v22.22.0",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "10.9.4"
  },
  "frameworks": [
    {
      "name": "react",
      "version": "19.1.1"
    }
  ],
  "databases": null,
  "betterAuth": {
    "version": "catalog:",
    "config": null
  }
}

Which area(s) are affected? (Select all that apply)

Client

Auth config (if applicable)

import { createAuthClient } from "better-auth/react";
import { expoClient } from "@better-auth/expo/client";
import * as SecureStore from "expo-secure-store";
import {
	adminClient,
	inferAdditionalFields,
	oneTimeTokenClient,
	usernameClient,
	emailOTPClient,
	phoneNumberClient,
	twoFactorClient,
	anonymousClient,
} from "better-auth/client/plugins";

export const authClient = createAuthClient({
	baseURL: process.env.EXPO_PUBLIC_BASE_URL,
	disableDefaultFetchPlugins: true,
	plugins: [
		expoClient({
			scheme: "test",
			storagePrefix: "test",
			storage: SecureStore,
		}),
		anonymousClient(),
		usernameClient(),
		emailOTPClient(),
		phoneNumberClient(),
		oneTimeTokenClient(),
		twoFactorClient(),
	],
});

Additional context

Root cause

After digging into the source, I found the issue in node_modules/better-auth/dist/client/query.mjs:

async onError(context) {
  const retryAttempts = typeof request.retry === "number" ? request.retry : request.retry?.attempts;
  const retryAttempt = request.retryAttempt || 0;

  if (retryAttempts && retryAttempt < retryAttempts) return;

  // No network error check — data is unconditionally set to null on any failure
  value.set({
    error: context.error,
    data: null, // <-- session wiped on network error
    isPending: false,
    isRefetching: false,
    refetch: value.value.refetch
  });

  await opts?.onError?.(context);
},
Originally created by @florian-deroo on GitHub (Mar 5, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/8420 Originally assigned to: @bytaesu on GitHub. ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Authenticate successfully on a mobile device (React Native / Expo) 2. Turn off internet (WiFi + cellular) 3. Navigate around the app — session is intact, useSession still returns data ✓ 4. Turn internet back on 5. → useSession immediately sets data: null → app redirects to login screen ✗ Bonus step to confirm the token is still valid: 6. Force-quit and reopen the app → session is automatically restored ✓ (proves the token in SecureStore was never invalidated server-side) ### Current vs. Expected behavior **Current behavior:** When a mobile device loses internet and reconnects, useSession sets data: null and triggers a logout — even though the session token in SecureStore is still perfectly valid. **Expected behavior:** A transient network failure during session revalidation should preserve the previously cached session data. Only an explicit 401 Unauthorized response from the server should invalidate the session and trigger a logout. ### What version of Better Auth are you using? 1.4.16 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 25.3.0: Wed Jan 28 20:49:24 PST 2026; root:xnu-12377.81.4~5/RELEASE_ARM64_T8132", "release": "25.3.0", "cpuCount": 10, "cpuModel": "Apple M4", "totalMemory": "24.00 GB", "freeMemory": "0.42 GB" }, "node": { "version": "v22.22.0", "env": "development" }, "packageManager": { "name": "npm", "version": "10.9.4" }, "frameworks": [ { "name": "react", "version": "19.1.1" } ], "databases": null, "betterAuth": { "version": "catalog:", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Client ### Auth config (if applicable) ```typescript import { createAuthClient } from "better-auth/react"; import { expoClient } from "@better-auth/expo/client"; import * as SecureStore from "expo-secure-store"; import { adminClient, inferAdditionalFields, oneTimeTokenClient, usernameClient, emailOTPClient, phoneNumberClient, twoFactorClient, anonymousClient, } from "better-auth/client/plugins"; export const authClient = createAuthClient({ baseURL: process.env.EXPO_PUBLIC_BASE_URL, disableDefaultFetchPlugins: true, plugins: [ expoClient({ scheme: "test", storagePrefix: "test", storage: SecureStore, }), anonymousClient(), usernameClient(), emailOTPClient(), phoneNumberClient(), oneTimeTokenClient(), twoFactorClient(), ], }); ``` ### Additional context ### Root cause After digging into the source, I found the issue in `node_modules/better-auth/dist/client/query.mjs`: ```js async onError(context) { const retryAttempts = typeof request.retry === "number" ? request.retry : request.retry?.attempts; const retryAttempt = request.retryAttempt || 0; if (retryAttempts && retryAttempt < retryAttempts) return; // No network error check — data is unconditionally set to null on any failure value.set({ error: context.error, data: null, // <-- session wiped on network error isPending: false, isRefetching: false, refetch: value.value.refetch }); await opts?.onError?.(context); }, ```
GiteaMirror added the lockedbug labels 2026-04-17 19:51:59 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Mar 5, 2026):

Hi @florian-deroo! I'm Dosu and I’m helping the better-auth team.

Your diagnosis is spot on. This is a known issue in Better Auth — the onError handler in client/query.ts unconditionally sets data: null after retries are exhausted, without distinguishing between network errors and actual auth failures (401s).

There's actually an existing issue (#987) documenting similar behavior — network failures like TypeError: Failed to fetch don't trigger retries at all (Better Fetch only retries HTTP response errors, not fetch exceptions), causing sessions to appear null.

Workaround: You could wrap your auth client calls with a custom fetch that has exponential backoff for network errors, as suggested in the issue comments. Another option is to handle the onError callback in your session usage to preserve the previous session data when the error is a network failure rather than a 401.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:4006468983 --> @dosubot[bot] commented on GitHub (Mar 5, 2026): <!-- Greeting --> Hi @florian-deroo! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> Your diagnosis is spot on. This is a known issue in Better Auth — the [`onError` handler in `client/query.ts`](https://github.com/better-auth/better-auth/blob/c15f6d9c0addd4218077f38c13be51cc17b69b42/packages/better-auth/src/client/query.ts#L74-L90) unconditionally sets `data: null` after retries are exhausted, without distinguishing between network errors and actual auth failures (401s). There's actually an [existing issue (#987)](https://github.com/better-auth/better-auth/issues/987) documenting similar behavior — network failures like `TypeError: Failed to fetch` don't trigger retries at all (Better Fetch only retries HTTP response errors, not fetch exceptions), causing sessions to appear null. **Workaround**: You could wrap your auth client calls with a custom fetch that has exponential backoff for network errors, as [suggested in the issue comments](https://github.com/better-auth/better-auth/issues/987#issuecomment-2558400053). Another option is to handle the `onError` callback in your session usage to preserve the previous session data when the error is a network failure rather than a 401. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/26406ecd-1d24-467a-8350-13eb28e0b594?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/26406ecd-1d24-467a-8350-13eb28e0b594?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/26406ecd-1d24-467a-8350-13eb28e0b594?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/26406ecd-1d24-467a-8350-13eb28e0b594?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/26406ecd-1d24-467a-8350-13eb28e0b594?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/26406ecd-1d24-467a-8350-13eb28e0b594?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/26406ecd-1d24-467a-8350-13eb28e0b594?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=Your%20diagnosis%20is%20spot%20on.%20This%20is%20a%20known%20issue%20in%20Better%20Auth%20%E2%80%94%20the%20%5B%60onError%60%20handler%20in%20%60client/query.ts%60%5D%28https%3A//github.com/better-auth/better-auth/blob/c15f6d9c0addd4218077f38c13be51cc17b69b42/packages/better-auth/src/client/query.ts%23L74-L90%29%20unconditionally%20sets%20%60data%3A%20null%60%20after%20retries%20are%20exhausted%2C%20without%20distinguishing%20between%20network%20errors%20and%20actual%20auth%20failures%20%28401s%29.%0A%0AThere%27s%20actually%20an%20%5Bexisting%20issue%20%28%23987%29%5D%28https%3A//github.com/better-auth/better-auth/issues/987%29%20documenting%20similar%20behavior%20%E2%80%94%20network%20failures%20like%20%60TypeError%3A%20Failed%20to%20fetch%60%20don%27t%20trigger%20retries%20at%20all%20%28Better%20Fetch%20only%20retries%20HTTP%20response%20errors%2C%20not%20fetch%20exceptions%29%2C%20causing%20sessions%20to%20appear%20null.%0A%0A%2A%2AWorkaround%2A%2A%3A%20You%20could%20wrap%20your%20auth%20client%20calls%20with%20a%20custom%20fetch%20that%20has%20exponential%20backoff%20for%20network%20errors%2C%20as%20%5Bsuggested%20in%20the%20issue%20comments%5D%28https%3A//github.com/better-auth/better-auth/issues/987%23issuecomment-2558400053%29.%20Another%20option%20is%20to%20handle%20the%20%60onError%60%20callback%20in%20your%20session%20usage%20to%20preserve%20the%20previous%20session%20data%20when%20the%20error%20is%20a%20network%20failure%20rather%20than%20a%20401.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/8420)
Author
Owner

@bytaesu commented on GitHub (Mar 6, 2026):

Hi @florian-deroo,

I tested this with an actual Expo app and made a fix!

However, since the change exists outside the Expo plugin, I'll review it again with the team before applying it.

<!-- gh-comment-id:4009174428 --> @bytaesu commented on GitHub (Mar 6, 2026): Hi @florian-deroo, I tested this with an actual Expo app and made a fix! However, since the change exists outside the Expo plugin, I'll review it again with the team before applying it.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#28408