Documentation

Errors and Troubleshooting

Check 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 check the futures that your calls return.

Every failure that the SDK detects is a CelerisException, an unchecked exception with a stable code(). Match the code with code() == ErrorCode.TIMEOUT or a switch, never on message text. Messages name the field and the rule that failed. They never repeat your input, a credential, or server text, and they never carry a cause. Errors that the server sends are a ServerErrorException instead.

1. Check the call you made

chat.publish(Payloads.text("Hello"))
    .whenComplete(
        (ignored, failure) -> {
          if (failure == null) {
            return;
          }

          Throwable cause = failure instanceof CompletionException ? failure.getCause() : failure;

          if (cause instanceof CelerisException sdkFailure
              && sdkFailure.code() == ErrorCode.NOT_CONNECTED) {
            System.out.println("Wait until connected before sending.");
          } else {
            System.out.println(cause.getMessage());
          }
        });

Methods that return a future never throw, except NullPointerException for a null argument. Every failure completes the future. join() rethrows the failure inside a CompletionException, and a dependent stage sees it wrapped too. Unwrap it with getCause(). Check connect(), publish, and presenceList where you call them. A failed initial connect, like a failed presence query, fails its future and does not also reach onError.

Other calls throw at once. ClientOptions.Builder.build(), client.channel, and channel.segment throw CONFIGURATION for invalid input. subscribe throws NOT_CONNECTED when the channel is closing or closed. A future that you cancel completes with the JDK's CancellationException. A future past an orTimeout deadline completes with TimeoutException. Neither exception is a CelerisException.

2. Handle server errors separately

Registration errorListener =
    channel
        .events()
        .onError(
            error -> {
              if (!(error instanceof ServerErrorException serverError)) {
                System.out.println(error.getMessage());

                return;
              }

              System.out.println(
                  serverError.type()
                      + " "
                      + serverError.subType().orElse("")
                      + " "
                      + serverError.getMessage());

              switch (serverError.type()) {
                case ServerErrorException.PERMISSION_DENIED_ERROR ->
                    System.out.println("Check the server-issued channel and segment permissions.");
                case ServerErrorException.MESSAGE_SIZE_LIMIT_ERROR ->
                    System.out.println("Reduce the payload size.");
                case ServerErrorException.RATE_LIMIT_ERROR ->
                    // The SDK pauses and resends recent commands by itself.
                    System.out.println("Rate limited; publish less often if this repeats.");
                default -> System.out.println("The server reported an error.");
              }
            });

// Later:
errorListener.close();

A ServerErrorException carries type(), subType(), getMessage(), and resource(). type() is a String, with constants for the six known types. subType() is an Optional that names the command it answers. getMessage() is the server's own text. resource() is an Optional of what that command names, such as the segment.

The exception has no code(). Match its type(). Error types are open-ended, so keep a default branch for types that a newer server adds. A resource can be a String, an Integer, a Long, or an unmodifiable List of these. Check its type before you use it.

A permission denial can identify a command and a segment. It does not identify which of several identical publishes caused the error. The SDK matches presence-query errors to their query, and those errors fail presenceList's future 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

CodeResponse
CONFIGURATIONFix the invalid argument or credential. A retry with unchanged input does not help
NOT_CONNECTEDWait for a connection, then try again. A publish waits in the queue only while the channel is reconnecting
TIMEOUTCheck the credential endpoint and network, or the query deadline
CANCELLEDThe channel closed, or a newer attempt replaced this one, during the operation. Do not restart it blindly
TRANSPORTCheck network and credential setup. This does not prove an authentication failure
BACKPRESSURESlow production. The publish queue (publishQueueSize, 64 by default) is full, or a presence query found the writer full or paused
OPERATION_IN_PROGRESSDo not start simultaneous connects or presence queries on one channel
DELIVERY_UNKNOWNAcceptance is uncertain. Do not blindly resend an irreversible operation
PROTOCOLOne message was dropped. The connection remains usable. field() and offset() locate the failure
ServerErrorExceptionAn error that the server sent. See step 2

ErrorCode.code() gives the name that every Celeris SDK uses for a code, such as NotConnected. The SDK catches a listener that throws and reports it through onError as TRANSPORT. The SDK does not report an error listener that throws. A null listener registration throws NullPointerException at once, as a programming error.

4. Diagnose common symptoms

SymptomFirst checks
First connect fails and nothing retriesThis is expected. Call connect() again from failed
Socket connects but messages are absentChannel/segment match, a held subscription, and read permission
Sender sees no received copyallowEcho defaults to false
Publish completes, then an error arrivesExpected for server refusals. Completion means only local acceptance
presenceList always fails with TIMEOUTA listener waits for it. Compose on the future or use your own thread
Presence events miss a userYou get events only for joins and leaves while you hold presence. Query a cluster-wide listing
The server denied credentials, but the error says TransportThe SDK reports every refused handshake as TRANSPORT
Messages are absent after recoveryReplay is bounded. Reload authoritative application state
presenceList fails with CONFIGURATIONpage must be at least 1 and perPage 1 to 100
A closed channel cannot reconnectObtain a new channel from the client
A program whose only work is message receipt exitsThe SDK's threads are daemon threads. Keep your main thread alive

See Reconnection and Recovery for which failures retry. Do not implement your own retry timer while the channel is in the reconnecting state.

Next: Server-side usage, or the Java client API for the full error surface.

We use Google cookies for analytics and for advertising measurement, only if you allow them. Until you choose, Google sets no cookies. Refer to our Cookie Policy.