apache/incubator-seata · error · FrameworkException

0109

0109

Error message

frameworkException

What it means

Thrown by the Seata remoting client when sendAsyncRequest(Channel, Object) is invoked with a null channel. Instead of silently dropping the message, the client wraps a dummy Throwable in a FrameworkException with code ChannelIsNotWritable (0109). It almost always means the RM/TM had no live connection to the TC when an async (one-way or merged) request was attempted.

Source

Thrown at core/src/main/java/org/apache/seata/core/rpc/netty/AbstractNettyRemotingClient.java:233

            return super.sendSync(channel, rpcMessage, timeoutMillis);
        }
    }

    @Override
    public Object sendSyncRequest(Channel channel, Object msg) throws TimeoutException {
        if (channel == null) {
            LOGGER.warn("sendSyncRequest nothing, caused by null channel.");
            return null;
        }
        RpcMessage rpcMessage = buildRequestMessage(msg, ProtocolConstants.MSGTYPE_RESQUEST_SYNC);
        return super.sendSync(channel, rpcMessage, this.getRpcRequestTimeout());
    }

    @Override
    public void sendAsyncRequest(Channel channel, Object msg) {
        if (channel == null) {
            LOGGER.warn("sendAsyncRequest nothing, caused by null channel.");
            throw new FrameworkException(
                    new Throwable("throw"), "frameworkException", FrameworkErrorCode.ChannelIsNotWritable);
        }
        RpcMessage rpcMessage = buildRequestMessage(
                msg,
                msg instanceof HeartbeatMessage
                        ? ProtocolConstants.MSGTYPE_HEARTBEAT_REQUEST
                        : ProtocolConstants.MSGTYPE_RESQUEST_ONEWAY);
        Object body = rpcMessage.getBody();
        if (body instanceof MergeMessage) {
            Integer parentId = rpcMessage.getId();
            mergeMsgMap.put(parentId, (MergeMessage) rpcMessage.getBody());
            if (body instanceof MergedWarpMessage) {
                for (Integer msgId : ((MergedWarpMessage) rpcMessage.getBody()).msgIds) {
                    childToParentMap.put(msgId, parentId);
                }
            }
        }
        super.sendAsync(channel, rpcMessage);

View on GitHub (pinned to e01f97c6db)

Solutions

  1. Check that the client is registered and the channel is active before sending: channel != null && channel.isActive()
  2. Verify the seata server is reachable and the client has completed TM/RM registration (look for 'register success' logs)
  3. If sending to a specific server, re-acquire the channel via the client channel manager instead of caching a Channel reference
  4. Increase reconnect logic coverage: ensure doReconnect runs for your transaction service group (registry config serviceGroup -> cluster mapping)

Example fix

// before
client.sendAsyncRequest(channel, branchRegisterRequest);

// after
if (channel != null && channel.isActive()) {
    client.sendAsyncRequest(channel, branchRegisterRequest);
} else {
    LOGGER.warn("channel inactive, skipping async request; waiting for reconnect");
}
Defensive patterns

Strategy: validation

Validate before calling

if (channel == null || !channel.isActive()) {
    LOGGER.warn("skip async request: channel null or inactive");
    return; // or trigger reconnect
}
client.sendAsyncRequest(channel, msg);

Try / catch

catch (FrameworkException e) {
    if (e.getErrCode() == FrameworkErrorCode.ChannelIsNotWritable) {
        // schedule reconnect and retry once the channel is back
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling AbstractNettyRemotingClient.sendAsyncRequest(channel, msg) where the channel argument is null; typical upstream paths are heartbeats or merged message sends after the server channel was closed/invalidated but the caller kept the stale reference.

Common situations: Seata server restart or network flap invalidating the client channel; the client has not yet finished reconnecting when a branch register/global report fires; proxies capturing a channel before registration completes.

Related errors


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