{"record":{"id":"d2e48541d5f132d4","repo":"apache/incubator-seata","slug":"0109-d2e485","errorCode":"0109","errorMessage":"frameworkException","messagePattern":"frameworkException","errorType":"exception","errorClass":"FrameworkException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/seata/core/rpc/netty/AbstractNettyRemotingClient.java","lineNumber":233,"sourceCode":"            return super.sendSync(channel, rpcMessage, timeoutMillis);\n        }\n    }\n\n    @Override\n    public Object sendSyncRequest(Channel channel, Object msg) throws TimeoutException {\n        if (channel == null) {\n            LOGGER.warn(\"sendSyncRequest nothing, caused by null channel.\");\n            return null;\n        }\n        RpcMessage rpcMessage = buildRequestMessage(msg, ProtocolConstants.MSGTYPE_RESQUEST_SYNC);\n        return super.sendSync(channel, rpcMessage, this.getRpcRequestTimeout());\n    }\n\n    @Override\n    public void sendAsyncRequest(Channel channel, Object msg) {\n        if (channel == null) {\n            LOGGER.warn(\"sendAsyncRequest nothing, caused by null channel.\");\n            throw new FrameworkException(\n                    new Throwable(\"throw\"), \"frameworkException\", FrameworkErrorCode.ChannelIsNotWritable);\n        }\n        RpcMessage rpcMessage = buildRequestMessage(\n                msg,\n                msg instanceof HeartbeatMessage\n                        ? ProtocolConstants.MSGTYPE_HEARTBEAT_REQUEST\n                        : ProtocolConstants.MSGTYPE_RESQUEST_ONEWAY);\n        Object body = rpcMessage.getBody();\n        if (body instanceof MergeMessage) {\n            Integer parentId = rpcMessage.getId();\n            mergeMsgMap.put(parentId, (MergeMessage) rpcMessage.getBody());\n            if (body instanceof MergedWarpMessage) {\n                for (Integer msgId : ((MergedWarpMessage) rpcMessage.getBody()).msgIds) {\n                    childToParentMap.put(msgId, parentId);\n                }\n            }\n        }\n        super.sendAsync(channel, rpcMessage);","sourceCodeStart":215,"sourceCodeEnd":251,"githubUrl":"https://github.com/apache/incubator-seata/blob/e01f97c6db397165050caa6764020410c2c8199a/core/src/main/java/org/apache/seata/core/rpc/netty/AbstractNettyRemotingClient.java#L215-L251","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check that the client is registered and the channel is active before sending: channel != null && channel.isActive()","Verify the seata server is reachable and the client has completed TM/RM registration (look for 'register success' logs)","If sending to a specific server, re-acquire the channel via the client channel manager instead of caching a Channel reference","Increase reconnect logic coverage: ensure doReconnect runs for your transaction service group (registry config serviceGroup -> cluster mapping)"],"exampleFix":"// before\nclient.sendAsyncRequest(channel, branchRegisterRequest);\n\n// after\nif (channel != null && channel.isActive()) {\n    client.sendAsyncRequest(channel, branchRegisterRequest);\n} else {\n    LOGGER.warn(\"channel inactive, skipping async request; waiting for reconnect\");\n}","handlingStrategy":"validation","validationCode":"if (channel == null || !channel.isActive()) {\n    LOGGER.warn(\"skip async request: channel null or inactive\");\n    return; // or trigger reconnect\n}\nclient.sendAsyncRequest(channel, msg);","typeGuard":null,"tryCatchPattern":"catch (FrameworkException e) {\n    if (e.getErrCode() == FrameworkErrorCode.ChannelIsNotWritable) {\n        // schedule reconnect and retry once the channel is back\n    } else { throw e; }\n}","preventionTips":["Never cache Channel references across requests; resolve per send","Watch client logs for 'register success' before issuing the first requests","Monitor channel active state in a health check when embedding seata client"],"tags":["network","netty","client","null-channel"],"backgroundTag":null,"analyzedSha":"e01f97c6db397165050caa6764020410c2c8199a","analyzedAt":"2026-08-14T10:23:53.097Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}