home-assistant / home-assistant/iOS
Intermittent severe UI slowdown with WebSocket reconnect/subscription races on iOS 27
- 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
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