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
- Call and await ConnectAndSubscribeAsync once before sending any commands; keep the returned task/loop alive.
- Check client socket state (or expose a property) before sending: only send when WebSocketState.Open.
- Implement reconnect with backoff: on this exception, dispose/recreate the client and re-run ConnectAndSubscribeAsync.
- 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
- Always await ConnectAndSubscribeAsync before any send call
- Monitor socket state and reconnect on close events
- Wrap long-running bots in a supervisor loop that recreates the client on failure
- Use a CancellationToken and background task to keep the receive loop alive
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)