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 listener once per channel, and also catch exceptions from the operations that you await.
Every error that the SDK raises or reports is a CelerisError with a stable code. Match on the code, never on the message text. Messages name the field and the rule that failed. They never repeat your input, a credential, or server text.
1. Catch the operation you started
from useceleris_client import CelerisConnectionError, text_payload
try:
await chat.publish(text_payload("Hello"))
except CelerisConnectionError as error:
if error.code == "NotConnected":
print("Wait until connected before sending.")
else:
print(error.code, error)
Catch connect(), publish(), and presence_list() where you await them. A failed initial connect raises to its caller and does not also reach on_error. The SDK likewise reports a failed presence query to its caller.
2. Handle server errors separately
from useceleris_client import ChannelError, ServerError
def handle(error: ChannelError) -> None:
if not isinstance(error, ServerError):
print(error.code, error)
return
print(error.type, error.sub_type, error)
if error.type == "PermissionDeniedError":
print("Check the server-issued channel and segment permissions.")
elif error.type == "MessageSizeLimitError":
print("Reduce the payload size.")
elif error.type == "RateLimitError":
# The SDK pauses and resends recent commands by itself.
print("Rate limited; publish less often if this repeats.")
else:
print("The server reported an error.")
stop_errors = channel.events().on_error(handle)
# Later:
stop_errors()
A ServerError carries type, sub_type, its message, and resource. The sub_type is the command it answers, or None. The resource is what that command names, such as the segment. Its code is "Server". Error types are open-ended, so retain a default branch for a newer server. A resource can be None, a str, an int, or a tuple of these. Check its type before you use it.
A permission denial names a command and a segment. It does not identify which of several identical publishes caused the error. The SDK correlates presence-query errors internally. Those errors raise from presence_list() instead. Server error text is for people, not for programs. Show it as text, never as raw HTML. Server errors leave the connection open.
3. Choose the right response
code | Response |
|---|---|
Configuration | Fix the invalid argument or credential. A retry with unchanged input does not help |
NotConnected | Wait for a connection, then try again. A publish waits in the queue only while the channel is reconnecting |
Timeout | Check the credential endpoint and network, or the query deadline |
Cancelled | The channel closed during the operation. Do not automatically restart canceled work |
Transport | Check network and credential setup. This does not prove an authentication failure. A listener that raised also reports Transport |
Backpressure | Slow production. The publish queue (publish_queue_size, 64 by default) is full, or a presence query found the writer full or paused |
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 |
Server | An error from the server. See step 2 |
When you cancel a task that you own, the task raises asyncio.CancelledError, not an SDK error. The SDK isolates a listener's exception and reports it through on_error as a listener failure. Catch errors in the tasks that your listeners start.
4. Diagnose common symptoms
| Symptom | First checks |
|---|---|
| First connect fails and nothing retries | This is expected. Call connect() again from failed |
The channel stops in failed after a drop | Read the last error from on_error. Recovery used all maximum_reconnect_attempts, or a failure that is not Transport or Timeout stopped it |
| Socket connects but messages are absent | The channel and segment names, a held subscription, and read permission |
| Sender sees no received copy | allow_echo defaults to False |
| Publish returns, then an error arrives | Expected for server refusals. The return means only local acceptance |
A listener registration raises ConfigurationError | The listener is an async def. Use a plain function that starts a task |
| 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 | The handshake status is not available to the SDK |
| Messages are absent after recovery | Replay is bounded. Reload authoritative application state |
Presence query raises Configuration | Use int: page 1 to 2147483647 and per_page 1 to 100 |
| A closed channel cannot reconnect | Obtain a new channel from the client |
See Reconnection and Recovery for which failures retry. Do not run your own retry loop while the channel is in the reconnecting state.
Next: Server-side usage, or the Python client API for the full error surface.