Android Widget sometimes all fail and require to open the HA application since HTTPS is enabled

Hi!
I’ve been facing this issue for month and couldn’t find a way to solve it.
I have multiple android widgets from Home Assistant and they were working fine until I enabled https access on my home assistant (and other services on my nas).
What happens is any widget action result in a fail until I open the home assistant application. Then they all work again for a random time (several hours usually). I have no issue on the companion app itself or on the HA website access on computers

I use NginX (through swag in a docker container) with a domain name and a valid certificate from letsencrypt (expires on 2024-04-17) and ddns through cloudflare.
I have set the local address parameter in HA to https://ha.mydomain.com:8123 that redirects to the address of the nas hosting HA (192.168.x.x)

I updated HA up to 2024.1.6
companion app is 2024.1.5-full

Here is the error in the application logs :

01-21 11:42:02.438  7605  7605 D ButtonWidget: Sending service call to Home Assistant
01-21 11:42:02.441  7605  7605 D ServerConnectionInfo: localUrl is: false, usesInternalSsid is: false, usesWifi is: true
01-21 11:42:02.473  7605  7605 E ButtonWidget: Could not send service call.
01-21 11:42:02.473  7605  7605 E ButtonWidget: io.homeassistant.companion.android.common.data.integration.IntegrationException: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at io.homeassistant.companion.android.common.data.integration.impl.IntegrationRepositoryImpl.callService(IntegrationRepositoryImpl.kt:307)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at io.homeassistant.companion.android.common.data.integration.impl.IntegrationRepositoryImpl$callService$1.invokeSuspend(Unknown Source:15)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at android.os.Handler.handleCallback(Handler.java:938)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at android.os.Handler.dispatchMessage(Handler.java:99)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at android.os.Looper.loopOnce(Looper.java:226)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at android.os.Looper.loop(Looper.java:313)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at android.app.ActivityThread.main(ActivityThread.java:8751)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at java.lang.reflect.Method.invoke(Native Method)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
01-21 11:42:02.473  7605  7605 E ButtonWidget: Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.SSLUtils.toSSLHandshakeException(SSLUtils.java:363)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.ConscryptEngine.convertException(ConscryptEngine.java:1134)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1089)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:876)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:747)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:712)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.processDataFromSocket(ConscryptEngineSocket.java:858)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.access$100(ConscryptEngineSocket.java:731)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.ConscryptEngineSocket.doHandshake(ConscryptEngineSocket.java:241)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.ConscryptEngineSocket.startHandshake(ConscryptEngineSocket.java:220)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.kt:379)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.kt:337)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:209)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:226)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:517)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
01-21 11:42:02.473  7605  7605 E ButtonWidget: 	at java.lang.Thread.run(Thread.java:920)
01-21 11:42:02.473  7605  7605 E ButtonWidget: Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
01-21 11:42:02.474  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:656)
01-21 11:42:02.474  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:505)
01-21 11:42:02.474  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:425)
01-21 11:42:02.474  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.TrustManagerImpl.getTrustedChainForServer(TrustManagerImpl.java:353)
01-21 11:42:02.474  7605  7605 E ButtonWidget: 	at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:94)
01-21 11:42:02.474  7605  7605 E ButtonWidget: 	at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:90)
01-21 11:42:02.474  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.ConscryptEngineSocket$2.checkServerTrusted(ConscryptEngineSocket.java:163)
01-21 11:42:02.474  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:255)
01-21 11:42:02.474  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.ConscryptEngine.verifyCertificateChain(ConscryptEngine.java:1638)
01-21 11:42:02.474  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.NativeCrypto.ENGINE_SSL_read_direct(Native Method)
01-21 11:42:02.474  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.NativeSsl.readDirectByteBuffer(NativeSsl.java:569)
01-21 11:42:02.474  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.ConscryptEngine.readPlaintextDataDirect(ConscryptEngine.java:1095)
01-21 11:42:02.474  7605  7605 E ButtonWidget: 	at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1079)
01-21 11:42:02.474  7605  7605 E ButtonWidget: 	... 27 more
01-21 11:42:02.474  7605  7605 E ButtonWidget: Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
01-21 11:42:02.474  7605  7605 E ButtonWidget: 	... 40 more
01-21 11:42:02.477  7605  7605 V Toast   : show: caller = io.homeassistant.companion.android.widgets.button.ButtonWidget$callConfiguredService$1.invokeSuspend:328 
01-21 11:42:02.477  7605  7605 V Toast   : show: focusDisplayId = 0, isFocusInDesktop = false mCustomDisplayId=-1 isDexDualMode=false
01-21 11:42:02.477  7605  7605 V Toast   : show: isActivityContext = false
01-21 11:42:02.484  7605  7605 D AppWidgetManager: partiallyUpdateAppWidget() appWidgetIds = [208]
01-21 11:42:03.492  7605  7605 I AppWidgetManager: updateAppWidget() appWidgetIds = [208]

The “Trust anchor not found” error indicates the Root certificate store doesn’t have Let’s Encrypt root certificate. What is the underlying OS of the device you are running? If it’s old, it may no longer be getting Root Certificate updates. If that’s the case, you’ll have to manually install the Root certificate. How that is done depends on the OS.

1 Like

Also, did you use the fullchain.pem file as the certificate in your setup? If not, you should try that. It contains the full path to the Root certificate.

If you’re talking about the phone, it’s android 12.
Also, the error is weird because it seems to find it fine after opening the companion app anyway because then the widgets work correctly right ?

Hard to say since you didn’t supply the URL you are using in the companion app.

I supplied the same one as the web local address : https://ha.mydomain.com:8123

So, you did, my bad. The error certainly points to a certificate chain problem.

ServerConnectionInfo: localUrl is: false, usesInternalSsid is: false, usesWifi is: true

That line seems to imply that the local URL is not in use. I believe you need to configure your internal SSID to match before the companion app will use the local URL.

Do you have “Home Assistant URL” configured? Do you have “Home Network WiFi SSID” configured?

the first one to https://ha.mydomain.com:8123
the second was not set, but I just set it to my wifi SSID so I’ll see if something changes

Sadly, I still have the issue :frowning:

were you able to solve it? I’m having still the same issue as you.
Also using Android 13, latest version of HA in a docker container running on a Synology NAS.
And the certificate is from ‘synology.me’ provided by Synology.

I had less issues by setting the application battery usage to unlimited in android (instead of optimized as it was). But it still sometimes fails with the same error.
As a reminder, this only happens if ssl/https is enabled

Correct, eventually I moved away from trying to get it working with a certificate…
I’m now using TailScale VPN which is more reliable and solves my problem and is a lot easier to configure.