home-assistant / home-assistant/iOS

Intermittent severe UI slowdown with WebSocket reconnect/subscription races on iOS 27

Open
#5,542 13 comments 1 reaction 0 assignees View on GitHub
Dominant language
Swift
Stars
2.4k
Forks
520
Avg merge
7h 27m
Merged PRs (30d)
264

Description

### Description

The Home Assistant iOS Companion App intermittently becomes extremely slow/unresponsive when resuming/opening the app. Investigation suggests this is not caused by Home Assistant API latency or simply by the number of entities, but correlates with WebSocket reconnect/subscription races inside the app/HAKit.

The issue is intermittent: normal app database refreshes complete in ~1.2–1.4 s, while problematic sessions can leave the app sluggish for tens of seconds during reconnect cycles.

### Environment

- Companion App: **2026.7.5 (build 2026.2744)**
- iOS: **27.0**
- Device: **iPhone14,5**
- Home Assistant Core: **2026.8.2**
- Connection during reproduced cases: local HTTP (`http://:8123`)
- Active HA states: ~**3,205**
- Entity registry: ~**4,383** entries (~3,200 active)
- Device registry: ~**500** devices
- `/api/states` payload: ~**2.01 MB**

### Symptoms

The app can be very fast in one session and extremely sluggish in another. During slow sessions, the logs repeatedly show WebSocket disconnect/reconnect activity and subscription bookkeeping errors such as:

```text
WebSocket: unable to find request for identifier HARequestIdentifier(rawValue: 60)
WebSocket: unable to find subscription for identifier HARequestIdentifier(rawValue: 60)
```

A recurring sequence looks like:

```text
WebSocket: Sending: {"id":60,"include":{"domains":["zone","person"]},"type":"subscribe_entities"}
WebSocket: Sending: {"id":61,"include":{"domains":["zone","person"]},"type":"subscribe_entities"}
WebSocket: Sending: {"id":62,"subscription":60,"type":"unsubscribe_events"}
WebSocket: Received: result success HARequestIdentifier(rawValue: 60)
WebSocket: unable to find request for identifier HARequestIdentifier(rawValue: 60)
WebSocket: Received: event: for HARequestIdentifier(rawValue: 60)
WebSocket: unable to find subscription for identifier HARequestIdentifier(rawValue: 60)
```

This pattern has occurred repeatedly with different request IDs.

There are also repeated reconnects such as:

```text
WebSocket: Peer closed
WebSocket: phase transition to disconnected(error: nil, forReset: false)
WebSocket: disconnecting; permanently: default, error: nil
WebSocket: phase transition to disconnected(error: nil, forReset: true)
WebSocket: connecting using HAConnectionInfo(...)
```

In one particularly slow cycle, HAKit performed a `lateFireReset`, the connection became non-viable and produced `POSIXErrorCode 57: Socket is not connected`; the app remained disconnected for roughly 39 seconds before reconnecting.

The problem appears especially around app activation/resume/reconnect. Multiple operations can overlap around the same time: WebSocket reconnect, token refresh, WebView restart, sensor updates and new `subscribe_entities` subscriptions.

### Server/API performance checked

The HA server itself appears healthy and fast.

A direct request from the HA host for the complete states payload returned:

```text
HTTP: 200
Download: 2106150 Bytes
Start Transfer: 0.016854s
Total: 0.018614s
States: 3205
```

A request observed from the app itself also completed in approximately 48–125 ms in tested sessions.

Normal Companion App database refresh example:

- Entity fetch: ~0.8–1.0 s
- Full database refresh: ~1.2–1.4 s
- ~3,160–3,175 entities processed

A previous abnormal entity fetch took ~10.75 s / ~13.8 s for the full update, but this was not consistently reproducible. Subsequent sessions with the same HA state count returned to ~1 s, so entity count alone does not appear to explain the intermittent slowdown.

### Troubleshooting already performed

1. **Checked Home Assistant entity/device registries**
- No orphaned config entries.
- ~3,200 active states.
- Total state payload only ~2 MB.

2. **Checked large states/attributes**
- A few custom sensors are large (largest ~116 KB), but the entire `/api/states` response remains ~2 MB and is delivered by HA in ~19 ms locally.

3. **Fixed invalid device-registry identifiers**
- Earlier logs contained:

```text
Failed to fetch device registry: underlying(HAKit.HADataError.incorrectType(key: "identifiers", expected: "Optional>>", ...))
```

- Problematic integrations/devices were identified and cleaned up.
- After cleanup, device registry loading succeeds, e.g. ~498 devices saved in ~0.076 s.
- The severe intermittent slowdown still occurs, so this appears to have been a separate issue.

4. **Reset Companion App entity database**
- Used the built-in **Reset App Entity Database** function.
- Database rebuilt successfully (~3163 entities).
- `unable to find request/subscription` behavior still occurs afterward.

5. **Local Push disabled**
- Local Push had separately produced `NEAppPushErrorDomain Code=3` and retry activity.
- It is now disabled.
- The severe slowdown and WebSocket subscription errors still reproduce, so Local Push does not appear to be the primary cause.

### Additional observation: access-token refresh

In at least one reconnect cycle the stored access token was already expired (~35 minutes) when the WebSocket connection was being rebuilt. Token refresh then ran concurrently with reconnect/subscription activity. The refresh succeeded, but this may be relevant to the race; it is not confirmed as the cause.

### Expected behavior

Opening/resuming the app should reuse or cleanly rebuild the WebSocket connection and subscriptions without duplicate subscriptions, stale request identifiers, or long reconnect stalls.

### Actual behavior

Intermittently, app activation/reconnect appears to create overlapping `subscribe_entities` subscriptions, immediately unsubscribe one of them, and then process responses/events for identifiers that HAKit no longer knows about. This correlates with periods where the UI feels extremely sluggish.

### Possible area to investigate

There may be a race in HAKit's subscription lifecycle during app foreground/resume or connection reset, particularly when several of these happen together:

- WebSocket reconnect/reset
- `subscribe_entities` recreation
- cancellation/unsubscription of the previous subscription
- WebView restart
- token refresh
- sensor/background updates

It may be useful to check whether subscription/request bookkeeping is cleared or replaced before pending responses from the previous subscription have been processed.

### Related issue

Issue #4475 contains the same `unable to find request` / `unable to find subscription` messages in a different scenario (NFC/Jetsam on a large HA installation), suggesting the messages may not be unique to this installation.

I can provide additional Companion App logs and reproduce/test specific scenarios if useful.

Contributor guide

Open the contributing guide

Research direction

Start in HAKit's WebSocket reconnect/reset and subscription bookkeeping paths, focusing on app foreground/resume, token refresh, and overlapping subscribe_entities operations. Reproduce an activation or reconnect cycle and inspect the logged request and subscription identifiers. Done means reconnects cleanly without duplicate subscriptions, stale-identifier errors, or long UI stalls.

Written by the indexing model from the issue text.

Assessment

Tech stack
ios, swift
Domain
mobile-dev, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.