JeffreySu/WeiXinMPSDK · error · InvalidOperationException

智能机器人长连接尚未建立。

Error message

智能机器人长连接尚未建立。 

What it means

SmartRobotWebSocketClient.SendCommandAsync throws this InvalidOperationException when the underlying ClientWebSocket's State is not Open. All public send helpers (SendPingAsync, SendMessageAsync, RespondAsync, RespondWelcomeAsync, UpdateResponseAsync) route through SendCommandAsync, so any command sent before ConnectAndSubscribeAsync completes or after the connection dropped fails. The library requires an established long-lived WebSocket connection before any frame is sent.

Solutions

  1. Call and await ConnectAndSubscribeAsync once before sending any commands; keep the returned task/loop alive.
  2. Check client socket state (or expose a property) before sending: only send when WebSocketState.Open.
  3. Implement reconnect with backoff: on this exception, dispose/recreate the client and re-run ConnectAndSubscribeAsync.
  4. Handle simultaneous closure by catching InvalidOperationException around send calls and re-establishing the connection.

Example fix

// before
var client = new SmartRobotWebSocketClient(url, token);
await client.SendPingAsync(); // throws 智能机器人长连接尚未建立。

// after
var client = new SmartRobotWebSocketClient(url, token);
await client.ConnectAndSubscribeAsync();
if (client.State == WebSocketState.Open)
{
    await client.SendPingAsync();
}
Defensive patterns

Strategy: type-guard

Validate before calling

// before sending
if (client.SocketState != WebSocketState.Open)
{
    await client.ConnectAndSubscribeAsync(); // reconnect
}

Type guard

bool CanSend(SmartRobotWebSocketClient c) => c != null && c.SocketState == WebSocketState.Open;

Try / catch

try { await client.SendPingAsync(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("长连接尚未建立"))
{
    await ReconnectAsync(); // re-run ConnectAndSubscribeAsync with backoff
}

Prevention

When it happens

Trigger: Calling SendPingAsync/SendMessageAsync/RespondAsync/RespondWelcomeAsync/UpdateResponseAsync/SendCommandAsync when _socket.State != WebSocketState.Open: before calling ConnectAndSubscribeAsync, before the connect handshake finishes, after the server closed the socket, or after a network drop.

Common situations: Fire-and-forget message sends in a bot service that never awaited the connect task; long-running bot processes where the WebSocket silently dropped; racing RespondAsync from a message callback before the subscribe loop opened; reusing a client instance whose connection died and not reconnecting.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of JeffreySu/WeiXinMPSDK@be573f6f94 (2026-09-12). Data as JSON: /api/errors/2b75b76d788c40e4. Report an issue: GitHub.

Appendix: source

Thrown at src/Senparc.Weixin.Work/Senparc.Weixin.Work/AdvancedAPIs/SmartRobot/SmartRobotWebSocketClient.cs:227

        /// </summary>
        /// <param name="requestId">智能机器人 WebSocket 请求 ID。</param>
        /// <param name="cancellationToken">取消令牌。</param>
        /// <returns>表示异步操作的任务。</returns>
        public Task SendPingAsync(string requestId = null, CancellationToken cancellationToken = default)
            => SendCommandAsync("ping", requestId ?? CreateRequestId(), null, cancellationToken);

        /// <summary>发送官方协议中的任意命令,可用于媒体分片上传等扩展命令。</summary>
        /// <param name="command">智能机器人 WebSocket 命令。</param>
        /// <param name="requestId">智能机器人 WebSocket 请求 ID。</param>
        /// <param name="body">命令正文;无正文的命令可传 <see langword="null"/>。</param>
        /// <param name="cancellationToken">取消令牌。</param>
        /// <returns>表示异步操作的任务。</returns>
        public async Task SendCommandAsync(string command, string requestId, object body,
            CancellationToken cancellationToken = default)
        {
            if (_socket.State != WebSocketState.Open)
            {
                throw new InvalidOperationException("智能机器人长连接尚未建立。 ");
            }

            var envelope = new SmartRobotSocketEnvelope
            {
                cmd = command,
                headers = new SmartRobotSocketHeaders { req_id = requestId },
                body = body
            };
            var bytes = Encoding.UTF8.GetBytes(envelope.ToJson());

            await _sendLock.WaitAsync(cancellationToken).ConfigureAwait(false);
            try
            {
                await _socket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true,
                    cancellationToken).ConfigureAwait(false);
            }
            finally
            {

View on GitHub (pinned to be573f6f94)