Skip to content

[PM-40402] SocketTimeoutException trying to connect to directly attached IPv6 addresses #7167

Description

@jfly

Steps To Reproduce

  1. Host a bitwarden server on your local network, accessible only over IPv6 (for example, some IPv6 ULA such as fdd4:aa51:eed9:426:9f99:938b:ebf9:7433, accessible via bitwarden.example.com, which you'll see the in stacktrace below.).
  2. With your android phone connected to the same local network: open bitwarden, on the login screen, select "Self-hosted", tap "Self-hosted", and enter the url of your server (in this example: https://bitwarden.example.com).
  3. Try to log in. You'll experience a delay after pressing "Continue" on the login screen, but you will eventually make it to the master password prompt screen.
  4. On the master password prompt screen: enter your master password and click "Log in with master password". After 10 seconds of "Logging in...", you'll get a popup that says "An error has occurred. We were unable to process your request. Please try again or contact us." If you click "Share error details", you'll see a stacktrace of a SocketTimeoutException (full trace below).

Expected Result

I expect to be able to log into my bitwarden server.

Actual Result

Here's the stack trace:

Stacktrace:
java.net.SocketTimeoutException: failed to connect to bitwarden.example.com/fdd4:aa51:eed9:426:9f99:938b:ebf9:7433 (port 443) from /fdd4:aa51:eed9:426:9f99:93b5:4d8:4a2e (port 38658) after 10000ms
	libcore.io.IoBridge.connectErrno(IoBridge.java:235)
	libcore.io.IoBridge.connect(IoBridge.java:179)
	java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:142)
	java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:390)
	java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:230)
	java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:212)
	java.net.SocksSocketImpl.connect(SocksSocketImpl.java:436)
	java.net.Socket.connect(Socket.java:646)
	ev1.i(r8-map-id-01e002bd0632bb5cc7de20747ff74afc7c2ccce2c9b98107678ea7daf8b79b97:77)
	ev1.d(r8-map-id-01e002bd0632bb5cc7de20747ff74afc7c2ccce2c9b98107678ea7daf8b79b97:38)
	r63.a(r8-map-id-01e002bd0632bb5cc7de20747ff74afc7c2ccce2c9b98107678ea7daf8b79b97:3)
	ia.a(r8-map-id-01e002bd0632bb5cc7de20747ff74afc7c2ccce2c9b98107678ea7daf8b79b97:67)
	ia.run(r8-map-id-01e002bd0632bb5cc7de20747ff74afc7c2ccce2c9b98107678ea7daf8b79b97:82)
	java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1100)
	java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	java.lang.Thread.run(Thread.java:1572)

Version: 2026.6.1 (21713)
Device: 📱 google Pixel 6 🤖 17@37 📦 prod -fdroid
CI: 🧱 commit: bitwarden/android/release/2026.6-rc57@e5ee43f68773f9fe20bec3dd664781d65b804967
💻 build source: bitwarden/android/actions/runs/28253991408/attempts/1

Screenshots or Videos

No response

Additional Context

The last version of the app where this worked was 2026.5.0. I tested 2026.6.1, 2026.6.0, and 2026.5.1 and confirmed they do not work.

I bisected and found that this was introduced by #6916, which upgraded the android sdk from 36 to 37. One consequence of sdk version 37 is that local network permission is now disabled by default. The bitwarden android app only requests local network access if the address we're connecting to is a "site local address", as returned by InetAddress.isSiteLocalAddress. Here are the implementations for ipv4 and ipv6:

However, I don't think the correct fix here is to just add "is ula?" to the check. I believe the intent of the code is to check "would android block connections to this ip address?", which android conveniently documents on their Local Network Definition page. Fun fact: this reveals that our ipv4 check is likely wrong as well (android blocks link local and CGNAT addresses, which are not detected by Inet4Address.isSiteLocalAddress). The IPv6 check is even harder to implement, as it's not just a comparison against static subnets, it depends on your routing table.

AFAICT, there's no easy way to implement this check (I've asked android for advice on this in https://issuetracker.google.com/issues/534973523). I think one correct way to do it is to just try and check for the error reason afterwards with android_getnetworkblockedreason, but I guess that requires trying and waiting for a timeout, and it requires using the NDK.

Perhaps the least odious approach would be to prompt the user for local network permission if they select "Self-hosted"? Or catching SocketTimeoutException and warning the user that they might need to grant the "Nearby devices" permission?

Build Version

2026.6.1 (21713)

What server are you connecting to?

Self-host

Self-host Server Version

I promise this is not relevant

Environment Details

  • Device: Google Pixel 6
  • Android version: 17

Issue Tracking Info

  • I understand that work is tracked outside of Github. A PR will be linked to this issue should one be opened to address it, but Bitwarden doesn't use fields like "assigned", "milestone", or "project" to track progress.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions