Errors and Troubleshooting
Catch operation failures, handle asynchronous server errors, and diagnose common SDK problems.
Before you start: follow Connection and lifecycle. Register an error handler once per channel, and also catch rejected operation promises.
1. Catch the operation you started
Use these patterns in web/app.js. The first replaces a publish handler's catch logic, and the second replaces the channel's error listener.
import { ConnectionError, textPayload } from "@useceleris/client";
try {
await chat.publish({ payload: textPayload("Hello") });
} catch (error) {
if (error instanceof ConnectionError && error.code === "NotConnected") {
console.warn("Wait until connected before sending.");
} else {
console.error(error.name, error.message);
}
}
Catch connect(), publish(), and presenceList() at their call sites. A failed initial connect rejects its caller and does not also fire onError. The SDK likewise reports a failed presence query to its caller.
2. Handle server errors separately
import { ServerError } from "@useceleris/client";
const stopErrors = channel.events().onError((error) => {
if (error instanceof ServerError) {
console.error(error.type, error.subType, error.message);
switch (error.type) {
case "PermissionDeniedError":
console.warn(
"Check the server-issued channel and segment permissions."
);
break;
case "MessageSizeLimitError":
console.warn("Reduce the payload size.");
break;
case "RateLimitError":
// The SDK pauses and resends recent commands by itself.
console.warn("Rate limited; reduce the publishing rate if this repeats.");
break;
default:
console.warn("The server reported an error.");
}
return;
}
console.error(error.code, error.message);
});
// Later:
stopErrors();
A ServerError contains type, subType, message, and resource. It has no SDK code. Error types are open-ended, so retain a default branch for a newer server. A resource can be null, a string, a number, a bigint, or a nested array of these. Check its type before you use it. Numeric resources are not necessarily timestamps.
A permission denial can identify a command and segment. It does not identify which of several otherwise identical publishes caused the error. The SDK correlates presence-query errors internally. Those errors reject their query instead.
Server error text is for people, not for programs. Show it as text, never as raw HTML. Do not put sensitive diagnostic details in shared logs. Server errors leave the connection open.
3. Choose the right response
| SDK code | Response |
|---|---|
Configuration | Fix invalid input or malformed credential output. A retry with unchanged input does not help |
NotConnected | Connect the channel, then try again. While the channel is reconnecting, a publish waits in the queue, but a presence query rejects |
Timeout | Check the credential endpoint/network or query deadline |
Cancelled | Usually an intentional abort or close. Do not automatically restart canceled work |
Transport | Check network and credential setup. This does not prove an authentication failure |
Backpressure | Slow production. The publish queue (publishQueueSize, 64 by default) is full, or a presence query found the writer full or paused after a rate limit. Wait, then retry |
OperationInProgress | Do not start simultaneous connects or presence queries on one channel |
DeliveryUnknown | Acceptance is uncertain. Do not blindly resend an irreversible operation |
ProtocolError | One frame was dropped. The connection remains usable |
The SDK isolates a listener's synchronous exception and reports it as a safe listener failure. Catch errors in asynchronous work that your listeners start.
4. Diagnose common symptoms
| Symptom | First checks |
|---|---|
| First connect fails and nothing retries | This is expected. Offer an explicit Retry action from failed |
| Socket connects but messages are absent | Channel/segment match, a held subscription, and read permission |
| Sender sees no received copy | allowEcho defaults to false |
| Publish resolves, then an error appears | Expected for server refusals. Resolution is only local acceptance |
| Presence events miss a user | You get events only for joins and leaves while you hold presence. Query a cluster-wide listing |
| The server denied credentials, but the error says Transport | WebSocket handshake status is unavailable to the SDK |
| Messages are absent after recovery | Replay is bounded. Reload authoritative application state |
Recovery stops in failed | onError reports the cause. The SDK used all maximumReconnectAttempts, or the failure does not retry. Offer an explicit Retry |
| Presence request rejects with Configuration | Use integer numbers: page 1–2147483647 and perPage 1–100. Do not pass strings or bigints |
| Arithmetic throws on mixed numeric types | Counts and page figures are numbers, and delivered timestamps are bigints. Do not mix them in arithmetic |
| JSON serialization throws | Convert bigint timestamps and bigint error-resource values to decimal strings. Keep counts as numbers |
| Closed channel cannot reconnect | Obtain a new channel handle |
See Reconnection and Recovery for which failures retry and how the budget works. Do not implement your own retry timer while the channel is in the reconnecting state.
Use the numeric-value and JSON examples when you store SDK data. Number(timestamp) is not a general serialization fix. It can silently discard precision.
A publish call on an unconnected channel then reaches the NotConnected branch and sends nothing. The error listener handles server errors separately by type and does not parse their message.
Next: Server-side usage, or the Client API for the full error surface.