Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Websocket nullpointerexceptions for subscribeTo #2852

Closed
dshokouhi opened this issue Sep 5, 2022 · 5 comments · Fixed by #2855
Closed

Websocket nullpointerexceptions for subscribeTo #2852

dshokouhi opened this issue Sep 5, 2022 · 5 comments · Fixed by #2855
Labels
bug Something isn't working

Comments

@dshokouhi
Copy link
Member

Home Assistant Android version:

beta-2646-84aa4454

Android version:

12/13

Phone model:

Pixel 5

Home Assistant version:

unknown

Last working Home Assistant release (if known):

unknown

Description of problem:

3 very similar null pointer errors in Sentry for the latest beta in the play store

Traceback (if applicable, to get the logs you may refer to: https://companion.home-assistant.io/docs/troubleshooting/faqs/#android-crash-logs):

java.lang.NullPointerException: null
    at io.homeassistant.companion.android.common.data.websocket.impl.WebSocketRepositoryImpl.subscribeTo(WebSocketRepositoryImpl.kt:244)
    at io.homeassistant.companion.android.common.data.websocket.impl.WebSocketRepositoryImpl.subscribeToEventsForType(WebSocketRepositoryImpl.kt:189)
    at io.homeassistant.companion.android.common.data.websocket.impl.WebSocketRepositoryImpl.getStateChanges(WebSocketRepositoryImpl.kt:177)
    at io.homeassistant.companion.android.common.data.integration.impl.IntegrationRepositoryImpl.getEntityUpdates(IntegrationRepositoryImpl.kt:578)
    at io.homeassistant.companion.android.widgets.BaseWidgetProvider$onScreenOn$1.invokeSuspend(BaseWidgetProvider.kt:86)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:201)
    at android.os.Looper.loop(Looper.java:288)
    at android.app.ActivityThread.main(ActivityThread.java:7911)
    at java.lang.reflect.Method.invoke(Method.java)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1009)
java.lang.NullPointerException: null
    at io.homeassistant.companion.android.common.data.websocket.impl.WebSocketRepositoryImpl.subscribeTo(WebSocketRepositoryImpl.kt:244)
    at io.homeassistant.companion.android.common.data.websocket.impl.WebSocketRepositoryImpl.subscribeToEventsForType(WebSocketRepositoryImpl.kt:189)
    at io.homeassistant.companion.android.common.data.websocket.impl.WebSocketRepositoryImpl.getStateChanges(WebSocketRepositoryImpl.kt:177)
    at io.homeassistant.companion.android.common.data.integration.impl.IntegrationRepositoryImpl.getEntityUpdates(IntegrationRepositoryImpl.kt:578)
    at io.homeassistant.companion.android.controls.HaControlsProviderService$createPublisherFor$1$1$request$1$2.invokeSuspend(HaControlsProviderService.kt:160)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
    at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
java.lang.NullPointerException: null
    at io.homeassistant.companion.android.common.data.websocket.impl.WebSocketRepositoryImpl.subscribeTo(WebSocketRepositoryImpl.kt:244)
    at io.homeassistant.companion.android.common.data.websocket.impl.WebSocketRepositoryImpl.getTemplateUpdates(WebSocketRepositoryImpl.kt:192)
    at io.homeassistant.companion.android.common.data.integration.impl.IntegrationRepositoryImpl.getTemplateUpdates(IntegrationRepositoryImpl.kt:180)
    at io.homeassistant.companion.android.widgets.template.TemplateWidget$onScreenOn$1.invokeSuspend(TemplateWidget.kt:143)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:226)
    at android.os.Looper.loop(Looper.java:313)
    at android.app.ActivityThread.main(ActivityThread.java:8663)
    at java.lang.reflect.Method.invoke(Method.java)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)

Screenshot of problem:

Additional information:

@dshokouhi dshokouhi added the bug Something isn't working label Sep 5, 2022
@jpelgrom
Copy link
Member

jpelgrom commented Sep 5, 2022

It's failing on this line:

The check before it uses the subscription ID instead of the flow to determine if it needs to create a new flow, so it looks like the subscription ID is still set while the flow is already null. I think it's a race condition in the unsubscribe logic because it sends the unsubscribe message in a suspending function and only removes the subscription ID when finished, whereas the flow is removed immediately:

if (eventSubscriptionId[subscribeMessage] != null) {
Log.d(TAG, "Unsubscribing from $type with data $data")
ioScope.launch {
sendMessage(
mapOf(
"type" to "unsubscribe_events",
"subscription" to eventSubscriptionId[subscribeMessage]!!
)
)
eventSubscriptionId.remove(subscribeMessage)
}
}
eventSubscriptionProducerScope.remove(subscribeMessage)
eventSubscriptionFlow.remove(subscribeMessage)

I'll do some tests to verify this and (hopefully) submit a fix soon.


Edit: add a delay(5000L) before the sendMessage that unsubscribes, add an entity state widget, quickly turn the screen off and on, and the exception has been reproduced on the latest version:

Stacktrace
2022-09-05 21:06:08.939 13983-13983/io.homeassistant.companion.android.debug E/AndroidRuntime: FATAL EXCEPTION: main
    Process: io.homeassistant.companion.android.debug, PID: 13983
    java.lang.NullPointerException
        at io.homeassistant.companion.android.common.data.websocket.impl.WebSocketRepositoryImpl.subscribeTo(WebSocketRepositoryImpl.kt:257)
        at io.homeassistant.companion.android.common.data.websocket.impl.WebSocketRepositoryImpl.subscribeToTrigger(WebSocketRepositoryImpl.kt:203)
        at io.homeassistant.companion.android.common.data.websocket.impl.WebSocketRepositoryImpl.getStateChanges(WebSocketRepositoryImpl.kt:182)
        at io.homeassistant.companion.android.common.data.integration.impl.IntegrationRepositoryImpl.getEntityUpdates(IntegrationRepositoryImpl.kt:615)
        at io.homeassistant.companion.android.widgets.BaseWidgetProvider$onScreenOn$1.invokeSuspend(BaseWidgetProvider.kt:108)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at android.os.Handler.handleCallback(Handler.java:942)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7898)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
    	Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@d80ecfc, Dispatchers.Main]

@jpelgrom
Copy link
Member

jpelgrom commented Sep 5, 2022

I believe this is the result of changes in #2801 so the latest stable release should be fine, but if you do spot it in other versions please mention it @dshokouhi !

@dshokouhi
Copy link
Member Author

I believe this is the result of changes in #2801 so the latest stable release should be fine, but if you do spot it in other versions please mention it @dshokouhi !

So far only in Beta 2646, lets see what happens after the next beta push on Saturday.

Also I think you are right about the race, most (if not all) of the events are related to screen turning on. I suspect if its not widgets it may be the users who have persistent connection set to Screen On and never changed the setting.

@jpelgrom
Copy link
Member

jpelgrom commented Sep 5, 2022

I suspect if its not widgets it may be the users who have persistent connection set to Screen On and never changed the setting.

The current stacktraces show widgets and device controls as the source. The persistent connection uses a different function to handle subscribing/unsubscribing (for now), so I would expect a slightly different stacktrace in that case, line 277 for beta-2646:

@dshokouhi
Copy link
Member Author

Found one more thats similar

java.lang.NullPointerException: null
    at io.homeassistant.companion.android.common.data.websocket.impl.WebSocketRepositoryImpl.subscribeTo(WebSocketRepositoryImpl.kt:244)
    at io.homeassistant.companion.android.common.data.websocket.impl.WebSocketRepositoryImpl.access$subscribeTo(WebSocketRepositoryImpl.kt:60)
    at io.homeassistant.companion.android.common.data.websocket.impl.WebSocketRepositoryImpl$subscribeTo$1.invokeSuspend
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
    at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants