appwrite / appwrite/sdk-for-react-native

🐛 Bug Report: INVALID_STATE_ERROR crash after background + network disconnect + foreground cycle

Aperta Adatta ai principianti
#101 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
bug
Lingua principale
TypeScript
Stelle
4.3k
Fork
36
Merge medio
2h 50m
PR unite (30g)
1

Descrizione

### 👟 Reproduction steps

1. Open the app with an active Realtime subscription
2. Disconnect the network
3. Send the app to the background
4. Reconnect the network and wait for the connection to fully re-establish
5. Bring the app back to the foreground

### 👍 Expected behavior

When an app using Realtime is sent to the background while the network is disconnected, and then brought back to the foreground after reconnecting, the app hard crashes with Uncaught Error: INVALID_STATE_ERROR originating from a WebSocket send() call inside the heartbeat interval.
The Realtime connection should resume normally.

### 👎 Actual Behavior

Hard crash: Uncaught Error: INVALID_STATE_ERROR thrown from socket.send() inside the heartbeat interval.

Most likely root cause:
There are two bugs in createHeartbeat() ([src/client.ts:259](https://github.com/appwrite/sdk-for-react-native/blob/main/src/client.ts#L259)):

```
createHeartbeat: () => {
if (this.realtime.heartbeat) {
clearTimeout(this.realtime.heartbeat); // ❌ Bug 1
}

this.realtime.heartbeat = window?.setInterval(() => {
this.realtime.socket?.send(JSON.stringify({ // ❌ Bug 2
type: 'ping'
}));
}, 20_000);
},
```
Bug 1 — clearTimeout cannot cancel a setInterval

this.realtime.heartbeat is assigned via setInterval, but clearTimeout is used to cancel it. These use separate timer registries — clearTimeout silently does nothing when given an interval ID. As a result, every call to createHeartbeat() (which happens on every open event) stacks a new interval on top of all previous ones without ever clearing them.

Bug 2 — no readyState guard before send()

The accumulated intervals all call socket.send() unconditionally. When the socket is in CLOSING (2) or CLOSED (3) state — which happens transiently during any reconnect — send() throws INVALID_STATE_ERROR.

Fix
```
createHeartbeat: () => {
if (this.realtime.heartbeat !== undefined) {
clearInterval(this.realtime.heartbeat); // ✅ matches setInterval
this.realtime.heartbeat = undefined;
}

this.realtime.heartbeat = window?.setInterval(() => {
if (this.realtime.socket?.readyState === WebSocket.OPEN) { // ✅ guard
this.realtime.socket.send(JSON.stringify({ type: 'ping' }));
}
}, 20_000);
},
```

### 🎲 Appwrite version

Version 0.7.x

### 💻 Operating system

MacOS

### 🧱 Your Environment

_No response_

### 👀 Have you spent some time to check if this issue has been raised before?

- [x] I checked and didn't find similar issue

### 🏢 Have you read the Code of Conduct?

- [x] I have read the [Code of Conduct](https://github.com/appwrite/appwrite/blob/HEAD/CODE_OF_CONDUCT.md)

Guida per i contributori

Apri la guida per i contributori

Direzione di ricerca

Inizia da src/client.ts:259, in createHeartbeat(), e traccia il modo in cui il socket Realtime e l'heartbeat vengono ricreati negli eventi open. Riproduci il ciclo background, disconnessione di rete, reconnect e foreground, quindi verifica che i timer dell'heartbeat non si accumulino e che l'invio sia sicuro durante la riconnessione del socket. Il lavoro è completato quando la connessione riprende senza un crash INVALID_STATE_ERROR.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
react-native, typescript
Ambito
api, mobile
Tipo di issue
Bug
Difficoltà
2/5
Tempo stimato
1-3 ore
Stato di attività
Tranquilla
Chiarezza
Specificata chiaramente
Idoneità per principianti
76/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.