Tutorial 10 of 18

How to Handle Reconnection and Errors Gracefully

Handle disconnect events, connection and send failures, retries, and offline UI with a safe application-owned reconnection policy.

8 min read

DNotifier SDKs surface connection failures, send errors, and disconnect events through thrown exceptions and onDisconnected callbacks. The SDK does not auto-reconnect — your app owns retry policy and offline UX.

Common Error Sources

PhaseErrorTypical cause
connect()Thrown exceptionWrong secret, invalid appId, plan issue, or network failure
send() / sendBinary()Not connectedCalled before connect() or after disconnect
send()Validation errorMissing senderId, receiverId, or receiverIds
TransportonDisconnectedNetwork loss, server close, or explicit disconnect()

Handle Disconnects

Register onDisconnected when constructing the client:

JavaScript / TypeScript

const notifier = new DNotifier({
  // ...
  onDisconnected: ({ code, reason }) => {
    console.log("Disconnected:", code, reason);
    showOfflineBanner();
    scheduleReconnect();
  },
});

Dart

onDisconnected: ({code, reason}) {
  print('Disconnected: $code $reason');
  showOfflineBanner();
  scheduleReconnect();
},

Reconnection Pattern

Always call connect() again after a disconnect. Do not call send() while isConnected is false.

JavaScript / TypeScript

let reconnectTimer = null;

function scheduleReconnect() {
  if (reconnectTimer) return;

  reconnectTimer = setTimeout(async () => {
    reconnectTimer = null;
    try {
      await notifier.connect();
      hideOfflineBanner();
    } catch (err) {
      console.error("Reconnect failed:", err.message);
      scheduleReconnect(); // exponential backoff recommended
    }
  }, 3000);
}

Dart

Future<void> reconnectWithBackoff(DNotifier notifier) async {
  for (var attempt = 1; attempt <= 5; attempt++) {
    try {
      await notifier.connect();
      return;
    } catch (e) {
      await Future.delayed(Duration(seconds: attempt * 2));
    }
  }
}

Send Error Handling

try {
  await notifier.send({
    senderId: myUserId,
    receiverId: peerUserId,
    data: { type: "text", text: "Hello" },
  });
} catch (err) {
  if (err.message === "Not connected") {
    await notifier.connect();
    // Retry once after the connection succeeds.
  } else {
    showSendFailedToast(err.message);
  }
}

Auth Failures at Connect

try {
  await notifier.connect();
} catch (err) {
  if (err.message.includes("401") || err.message.includes("auth")) {
    promptReLogin(); // Refresh credentials from your backend.
  } else {
    showNetworkError();
  }
}

Chunk Reassembly Failures

If the connection drops while a large message is chunking:

  • Partial chunks are discarded on the receiver.
  • The sender should retry the full send() after reconnecting.
  • Consider showing "Message failed to send" in the chat UI with a retry button.

Explicit Disconnect

notifier.disconnect();
// isConnected === false; onDisconnected fires

Use an explicit disconnect when the user logs out or your app tears down the realtime session.

Production Checklist

  • Listen to onDisconnected — update the UI and pause sends.
  • Use exponential backoff — avoid hammering authentication on flaky networks.
  • Load history idempotently — after reconnecting, call fetchChatHistory to fill gaps.
  • Deduplicate by metadata.id if your app retries sends.