dotnet/aspnetcore · error · RuntimeException

Trying to send and message while the connection is not activ

Error message

Trying to send and message while the connection is not active.

What it means

A RuntimeException thrown by the internal sendHubMessageWithLock helper when the HubConnection is not CONNECTED at the moment a hub message (ping, invocation, stream item, close, cancel, completion) is about to be written. This is an internal guard beneath the public send/invoke/stream APIs and also covers messages produced by the ping timer and by upload-stream forwarding. The message text contains a typo ('send and message') but the intent is the standard 'not active' guard.

Source

Thrown at src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/HubConnection.java:828

            sendInvocationMessage(method, args, invocationId, true);
            return observable.doOnDispose(() -> {
                if (subscriptionCount.decrementAndGet() == 0) {
                    CancelInvocationMessage cancelInvocationMessage = new CancelInvocationMessage(null, invocationId);
                    sendHubMessageWithLock(cancelInvocationMessage);
                    connectionState.tryRemoveInvocation(invocationId);
                    subject.onComplete();
                }
            });
        } finally {
            this.state.unlock();
        }
    }

    private void sendHubMessageWithLock(HubMessage message) {
        this.state.lock();
        try {
            if (this.state.getHubConnectionState() != HubConnectionState.CONNECTED) {
                throw new RuntimeException("Trying to send and message while the connection is not active.");
            }
            ByteBuffer serializedMessage = protocol.writeMessage(message);
            if (message.getMessageType() == HubMessageType.INVOCATION) {
                logger.debug("Sending {} message '{}'.", message.getMessageType().name(), ((InvocationMessage)message).getInvocationId());
            } else  if (message.getMessageType() == HubMessageType.STREAM_INVOCATION) {
                logger.debug("Sending {} message '{}'.", message.getMessageType().name(), ((StreamInvocationMessage)message).getInvocationId());
            } else {
                logger.debug("Sending {} message.", message.getMessageType().name());
            }

            ConnectionState connectionState = this.state.getConnectionStateUnsynchronized(false);
            connectionState.transport.send(serializedMessage).subscribeWith(CompletableSubject.create());
            connectionState.resetKeepAlive();
        } finally {
            this.state.unlock();
        }
    }

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Ensure stop() fully completes before disposing resources or triggering further sends.
  2. If seen from the ping timer, verify there is no race between stop() and the timer (this is largely internal; report if reproducible in normal usage).
  3. Dispose upload-stream Observables when the connection closes so they stop emitting into a dead connection.
  4. Avoid calling connection methods after onClosed fires.

Example fix

// before: upload stream keeps emitting after disconnect
Observable<Data> src = hotObservable;
connection.send("Upload", src);
// src keeps emitting after connection.stop() -> sendHubMessageWithLock throws

// after: dispose the source on close
connection.onClosed(ex -> srcSubscription.dispose());
Defensive patterns

Strategy: validation

Validate before calling

// Internal guard; the practical user-side guard is to avoid emitting into a closed connection.
// Dispose upload-stream sources on close.
connection.onClosed(ex -> uploadStreamSubscription.dispose());

Type guard

boolean connectionActive = connection.getConnectionState() == HubConnectionState.CONNECTED;

Try / catch

// This error is internal and surfaces through transport/onError paths. Catch at the
// subscription boundary:
someObservable.subscribe(item -> {
    if (connection.getConnectionState() == HubConnectionState.CONNECTED) {
        connection.send("Upload", item);
    }
}, error -> { /* log */ });

Prevention

When it happens

Trigger: Reached when sendHubMessageWithLock is invoked while state != CONNECTED: e.g. the ping timer fires after disconnect, an upload-stream's onNext races with stop(), or send/invoke/stream call paths reach here after the public guard already threw (defensive). Also when onClosed-triggered CloseMessage send is attempted mid-teardown.

Common situations: The keep-alive ping timer fires concurrently with stop() and attempts to send a Ping after state moved to DISCONNECTED. An Observable-based upload stream emits an item after the connection dropped. Internal cleanup paths sending CloseMessage during teardown.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/1cf8c08b74c62329. Report an issue: GitHub.