Skip to content

[Bug]: Android app doesn't handle network changes gracefully, particularly bad network connectivity #12573

Description

@MarkGStacey

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/web

Steps to reproduce

Start the app on cellular, move into an area and connect to WiFi, and move to a place where the cellular signal is lost. Connections need to be manually reconnected

Expected behavior

App should detect network change and re establish web sockets

Actual behavior

Phone doesn't detect network change

Impact

Major degradation or frequent failure

Version or commit

No response

Environment

No response

Logs or stack traces

Screenshots, recordings, or supporting files

No response

Workaround

No response

Activity

  1. added
    bugSomething is broken or behaving incorrectly.
    needs-triageIssue needs maintainer review and initial categorization.
    on Sep 19, 2026
  2. juliusmarminge commented on Sep 19, 2026

    @juliusmarminge
    Member

    Triage

    This is a real mobile reconnect gap on current main. The form listed apps/web, but the title and repro are the Android app. No existing issue looks like a duplicate.

    What happens

    The connection stack treats the network as a boolean: unknown / offline / online. Mobile maps expo-network down to that using only isConnected, and ignores interface type (wifi vs cellular):

    function networkStatus(state: Network.NetworkState): "unknown" | "offline" | "online" {
    if (state.isConnected === false) {
    return "offline";
    }
    if (state.isConnected === true) {
    return "online";
    }
    return "unknown";
    }

    A connected session is only torn down on a network event when the status becomes offline:

    case "NetworkChanged":
    if (next.network === "offline") {
    return false;
    }
    break;

    Same-status updates are dropped, so CELLULAR → WIFI (or “Wi‑Fi up, then cell drops”) never becomes a reconnect signal if isConnected stays true:

    yield* connectivity.changes.pipe(
    Stream.runForEach((network) =>
    Ref.modify(intent, (current) =>
    current.network === network ? [false, current] : ([true, { ...current, network }] as const),
    ).pipe(
    Effect.flatMap((changed) =>
    changed ? signal({ _tag: "NetworkChanged", network }) : Effect.void,
    ),
    ),
    ),
    Effect.forkScoped,

    The live WebSocket is not probed on a path change. A liveness probe / forced replace happens on app resume, not while the app stays in the foreground:

    export function mobileApplicationActiveWakeup(
    backgroundedAtMs: number | null,
    activeAtMs: number,
    ): MobileApplicationActiveWakeup {
    return backgroundedAtMs !== null &&
    activeAtMs - backgroundedAtMs >= MOBILE_BACKGROUND_RECONNECT_AFTER_MS
    ? "application-active-reconnect"
    : "application-active-probe";
    }

    Session death is only observed from socket/subscription close. There is no client ping timeout that would notice a half-open socket still bound to the old interface:

    closed: Effect.raceFirst(
    Deferred.await(disconnected),
    Deferred.await(configSubscriptionClosed),
    ),

    That matches the report: start on cell, join Wi‑Fi, lose cell, stay “online,” and the environment has to be reconnected by hand. Android dual-stack / default-route handoff is the usual trigger; iOS can hit the same shared path.

    Related work

    Workaround

    Background the app and come back (a resume of 10s+ forces application-active-reconnect), or reconnect the environment from the UI.

    Helpful extras (not blocking)

    App version, Android version, and whether this is LAN / Tailscale / T3 Connect. Also whether the app stayed in the foreground the whole time.

  3. added
    acceptedfeature request accepted
    via-triageFiled through npx t3 triage
    and removed
    needs-triageIssue needs maintainer review and initial categorization.
    on Sep 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions