Skip to content

NO_NETWORK_CONNECTION error when Always-On VPN is active (login and server reachability fail despite working connectivity) #17072

@ashutler

Description

@ashutler

⚠️ Before posting ⚠️

  • This is a bug, not a question or an enhancement.
  • I've searched for similar issues and didn't find a duplicate.
  • I've written a clear and descriptive title for this issue, not just "Bug" or "Crash".
  • I agree to follow Nextcloud's Code of Conduct.

Steps to reproduce

  1. On an Android 16 device, go to Settings → Network & Internet → VPN
  2. Configure a VPN and enable Always-On VPN (optionally also enable Block connections without VPN)
  3. Connect the VPN and confirm that a browser can reach external sites
  4. Open the Nextcloud app and attempt to add an account or sync
    NOTE: The VPN network hosts a NextCloud server however, is isolated from the Internet so the NextCloud does not have Internet access. The VPN app I am using is the Samsung secure VPN (com.samsung.sVpn) but I believe this would happen with any VPN client on an Internet isolated network.

Expected behaviour

The app connects to the Nextcloud server normally. VPN transport should be treated as a valid, connected network — the same way a Wi-Fi or cellular connection is treated.

Actual behaviour

The app displays a "No network connection" error. No network request is attempted. The login/sync flow is completely blocked despite the device having working internet connectivity through the VPN.

Android version

Android 16 (April 5th Patch level)

Device brand and model

Samsung XCover6 Pro

Stock or custom OS?

Stock

Nextcloud android app version

33.1.0

Nextcloud server version

30.0.6

Using a reverse proxy?

Yes

Android logs

No response

Server error logs

Additional information

Root Cause

There are two distinct failure paths, both stemming from how GetStatusRemoteOperation (in the android-library) determines whether the device is online.

Path 1 — isOnline() returns false before any network request is made

GetStatusRemoteOperation.run() calls isOnline() as a pre-flight check and returns NO_NETWORK_CONNECTION immediately if it returns false.

The current isOnline() implementation on Android M+ uses the deprecated ConnectivityManager.getActiveNetworkInfo()?.isConnectedOrConnecting(). Under Always-On VPN, the active network is the VPN interface. In certain VPN configurations (particularly lockdown/strict mode) isConnectedOrConnecting() can
return false for the VPN interface even when it is carrying traffic — causing isOnline() to return false and the operation to abort before touching the network.

Additionally, ConnectivityServiceImpl.isNetworkAndServerAvailable() (in the app itself) checks for NET_CAPABILITY_INTERNET on the active network. In strict Always-On VPN lockdown mode, some VPN implementations do not advertise NET_CAPABILITY_INTERNET on the VPN network object, causing this check to fail
and connectivity to be reported as unavailable.

Path 2 — DNS resolution fails through the VPN tunnel

Even when isOnline() returns true, the HTTP client may throw UnknownHostException when trying to resolve the server hostname. The RemoteOperationResult(Exception e) constructor maps UnknownHostException directly to NO_NETWORK_CONNECTION. Under Always-On VPN lockdown, DNS traffic that bypasses the VPN
tunnel is blocked by Android, so if the VPN's DNS configuration is strict, hostname resolution can fail.


Analysis

The modern, correct way to check connectivity on Android M+ is via NetworkCapabilities, not the deprecated NetworkInfo APIs. Specifically, a device connected through a VPN will have:

  • NetworkCapabilities.TRANSPORT_VPN set on the active network's capabilities
  • NetworkCapabilities.NET_CAPABILITY_INTERNET may or may not be set depending on VPN implementation

The pre-flight isOnline() check should treat a network with TRANSPORT_VPN as online (provided it also has NET_CAPABILITY_INTERNET), rather than falling through to the deprecated getActiveNetworkInfo() path which misreports VPN state.

// Current (problematic) approach in GetStatusRemoteOperation:
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
return networkInfo != null && networkInfo.isConnectedOrConnecting(); // unreliable for VPN

// Corrected approach on API 23+:
Network activeNetwork = connectivityManager.getActiveNetwork();
NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(activeNetwork);
boolean hasInternet = capabilities != null && capabilities.hasCapability(NET_CAPABILITY_INTERNET);
boolean hasVpn = capabilities != null && capabilities.hasTransport(TRANSPORT_VPN);
return hasInternet || hasVpn; // treat VPN transport as online


Device / Environment

Additionally, ConnectivityServiceImpl.isNetworkAndServerAvailable() (in the app itself) checks for NET_CAPABILITY_INTERNET on the active network. In strict Always-On VPN lockdown mode, some VPN implementations do not advertise NET_CAPABILITY_INTERNET on the VPN network object, causing this check to fail
and connectivity to be reported as unavailable.

Path 2 — DNS resolution fails through the VPN tunnel

Even when isOnline() returns true, the HTTP client may throw UnknownHostException when trying to resolve the server hostname. The RemoteOperationResult(Exception e) constructor maps UnknownHostException directly to NO_NETWORK_CONNECTION. Under Always-On VPN lockdown, DNS traffic that bypasses the VPN
tunnel is blocked by Android, so if the VPN's DNS configuration is strict, hostname resolution can fail.


Analysis

The modern, correct way to check connectivity on Android M+ is via NetworkCapabilities, not the deprecated NetworkInfo APIs. Specifically, a device connected through a VPN will have:

  • NetworkCapabilities.TRANSPORT_VPN set on the active network's capabilities
  • NetworkCapabilities.NET_CAPABILITY_INTERNET may or may not be set depending on VPN implementation

The pre-flight isOnline() check should treat a network with TRANSPORT_VPN as online (provided it also has NET_CAPABILITY_INTERNET), rather than falling through to the deprecated getActiveNetworkInfo() path which misreports VPN state.

// Current (problematic) approach in GetStatusRemoteOperation:
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
return networkInfo != null && networkInfo.isConnectedOrConnecting(); // unreliable for VPN

// Corrected approach on API 23+:
Network activeNetwork = connectivityManager.getActiveNetwork();
NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(activeNetwork);
boolean hasInternet = capabilities != null && capabilities.hasCapability(NET_CAPABILITY_INTERNET);
boolean hasVpn = capabilities != null && capabilities.hasTransport(TRANSPORT_VPN);
return hasInternet || hasVpn; // treat VPN transport as online


Device / Environment

  • Android version: 16 — uses NetworkCapabilities API path
  • Nextcloud app version: reproducible on current master
  • VPN type: Any Always-On VPN; more likely to trigger with "Block connections without VPN" (lockdown) enabled
  • Nextcloud server version: Any

Additional Context

ConnectivityServiceImpl.isConnected() in the app already correctly handles VPN — it explicitly checks hasTransport(NetworkCapabilities.TRANSPORT_VPN) and returns true. The inconsistency is that the library's GetStatusRemoteOperation.isOnline() does not apply the same logic.

This issue is distinct from the walled-garden / captive portal check (isInternetWalled()), which correctly skips active probing for non-Wi-Fi connections including VPN.

Metadata

Metadata

Assignees

No one assigned

    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