apache/incubator-seata · error · IOException

client is not connected

Error message

client is not connected

What it means

Server-side error from AbstractNettyRemotingServer.sendSyncRequest(Channel, Object): the caller passed a null channel, so the TC refuses to build and send the sync RPC. It is a programming/lookup bug on the server side rather than a network condition — the channel reference must be obtained (and non-null) before this overload is used.

Source

Thrown at core/src/main/java/org/apache/seata/core/rpc/netty/AbstractNettyRemotingServer.java:84

        serverBootstrap = new NettyServerBootstrap(nettyServerConfig);
        serverBootstrap.setChannelHandlers(new ServerHandler());
    }

    @Override
    public Object sendSyncRequest(String resourceId, String clientId, Object msg, boolean tryOtherApp)
            throws TimeoutException, IOException {
        Channel channel = ChannelManager.getChannel(resourceId, clientId, tryOtherApp);
        if (channel == null) {
            throw new IOException("rm client is not connected. dbkey:" + resourceId + ",clientId:" + clientId);
        }
        RpcMessage rpcMessage = buildRequestMessage(msg, ProtocolConstants.MSGTYPE_RESQUEST_SYNC);
        return super.sendSync(channel, rpcMessage, NettyServerConfig.getRpcRequestTimeout());
    }

    @Override
    public Object sendSyncRequest(Channel channel, Object msg) throws TimeoutException, IOException {
        if (channel == null) {
            throw new IOException("client is not connected");
        }
        RpcMessage rpcMessage = buildRequestMessage(msg, ProtocolConstants.MSGTYPE_RESQUEST_SYNC);
        return super.sendSync(channel, rpcMessage, NettyServerConfig.getRpcRequestTimeout());
    }

    @Override
    public void sendAsyncRequest(Channel channel, Object msg) throws IOException {
        if (channel == null) {
            throw new IOException("client is not connected");
        }
        RpcMessage rpcMessage = buildRequestMessage(msg, ProtocolConstants.MSGTYPE_RESQUEST_ONEWAY);
        super.sendAsync(channel, rpcMessage);
    }

    @Override
    public void sendAsyncResponse(RpcMessage rpcMessage, Channel channel, Object msg) {
        Channel clientChannel = channel;
        if (!(msg instanceof HeartbeatMessage)) {

View on GitHub (pinned to e01f97c6db)

Solutions

  1. Fetch the channel via ChannelManager with the resourceId/clientId overload that throws a descriptive error, instead of managing channels manually
  2. Guard with a null check and re-resolve the channel before retrying the send
  3. If writing a custom processor, follow the existing processors: they resolve the channel from the incoming message context, never cache it

Example fix

// before
server.sendSyncRequest(channel, request); // channel may be null

// after
Channel channel = ChannelManager.getChannel(resourceId, clientId, tryOtherApp);
if (channel == null || !channel.isActive()) {
    throw new IOException("client is not connected: " + clientId);
}
server.sendSyncRequest(channel, request);
Defensive patterns

Strategy: validation

Validate before calling

Channel ch = ChannelManager.getChannel(resourceId, clientId, tryOtherApp);
if (ch == null) throw new IOException("client not connected: " + clientId);
server.sendSyncRequest(ch, msg);

Try / catch

catch (IOException e) {
    if ("client is not connected".equals(e.getMessage())) { /* re-resolve channel and retry once */ }
}

Prevention

When it happens

Trigger: Calling sendSyncRequest((Channel) null, msg) directly, or passing a channel variable that a prior lookup (e.g. ChannelManager.getChannel) returned null for without a null check.

Common situations: Custom server-side code or a fork of seata-server that fetches a client channel and forwards it without checking; races where the channel was closed and nulled between fetch and send.

Related errors


AI-assisted analysis of apache/incubator-seata@e01f97c6db (2026-08-14). Data as JSON: /api/errors/3886efd86abc31ba. Report an issue: GitHub.