{"record":{"id":"fa421c13a7ccd20a","repo":"alibaba/canal","slug":"can-t-create-socket","errorCode":null,"errorMessage":"can't create socket!","messagePattern":"can't create socket!","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"critical","filePath":"driver/src/main/java/com/alibaba/otter/canal/parse/driver/mysql/socket/NettySocketChannelPool.java","lineNumber":66,"sourceCode":"\r\n                @Override\r\n                protected void initChannel(Channel ch) throws Exception {\r\n                    ch.pipeline().addLast(new BusinessHandler());// 命令过滤和handler添加管理\r\n                }\r\n            });\r\n    }\r\n\r\n    public static SocketChannel open(SocketAddress address) throws Exception {\r\n        SocketChannel socket = null;\r\n        ChannelFuture future = boot.connect(address).sync();\r\n\r\n        if (future.isSuccess()) {\r\n            future.channel().pipeline().get(BusinessHandler.class).latch.await();\r\n            socket = chManager.get(future.channel());\r\n        }\r\n\r\n        if (null == socket) {\r\n            throw new IOException(\"can't create socket!\");\r\n        }\r\n\r\n        return socket;\r\n    }\r\n\r\n    public static class BusinessHandler extends SimpleChannelInboundHandler<ByteBuf> {\r\n\r\n        private NettySocketChannel   socket = null;\r\n        private final CountDownLatch latch  = new CountDownLatch(1);\r\n\r\n        @Override\r\n        public void channelInactive(ChannelHandlerContext ctx) throws Exception {\r\n            socket.setChannel(null);\r\n            chManager.remove(ctx.channel());// 移除\r\n            super.channelInactive(ctx);\r\n        }\r\n\r\n        @Override\r","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/alibaba/canal/blob/87be50e87686a3e8af08c368d0e1ffd1f59eb04a/driver/src/main/java/com/alibaba/otter/canal/parse/driver/mysql/socket/NettySocketChannelPool.java#L48-L84","documentation":"Thrown by NettySocketChannelPool.open(SocketAddress) when boot.connect(address).sync() reports success (or the channelActive latch counted down) but chManager.get(future.channel()) still returns null — meaning no NettySocketChannel was registered for that netty Channel. Registration happens in BusinessHandler.channelActive (line 85-91); if channelActive never fires, or fires and is immediately followed by channelInactive (which removes it from chManager), the lookup returns null and open() rejects the socket. Declared as `throws Exception` from open().","triggerScenarios":"Calling SocketChannelPool.open() with canal.socketChannel=netty (or NettySocketChannelPool.open directly) when the TCP connect succeeds at the netty layer but the BusinessHandler.channelActive callback has not registered the wrapper — e.g. handler threw, or the channel went inactive between the connect and the chManager.get call.","commonSituations":"MySQL reachable at TCP level but immediately closing the link (host allowlist, max_connections reached, server shutdown in progress); a race where channelInactive fires right after channelActive (connection dropped during handshake); netty EventLoop blocked/slow so channelActive hasn't run by the time latch.await returns; SSL/TLS middlebox accepting TCP then resetting.","solutions":["Verify MySQL is actually accepting connections: connect from the canal host with the mysql CLI using the same user/host/port — a refused/immediately-closed session reproduces the symptom.","Check MySQL max_connections and the account's max_user_connections — exhaustion causes immediate post-connect drops.","Look in the canal log for the BusinessHandler.exceptionCaught or channelInactive lines immediately preceding the failure — they identify the real cause (handler exception, remote reset).","Confirm the target host:port is correct and that no TCP proxy/LB is accepting the connection then closing it (e.g. wrong upstream on an LB).","As a workaround, switch the socket implementation: set canal.socketChannel=bio (default) to use BioSocketChannelPool, which has a simpler connect path and surfaces the underlying socket error directly."],"exampleFix":"# before: netty socket channel\n canal.socketChannel = netty\n\n# after: fall back to bio (default) which exposes the real socket error\n# (remove the line or set explicitly)\ncanal.socketChannel = bio","handlingStrategy":"try-catch","validationCode":"// Validate reachability before asking canal to open the netty socket\ntry (java.net.Socket s = new java.net.Socket()) {\n    s.connect(new InetSocketAddress(mysqlHost, mysqlPort), 3000);\n} catch (IOException e) {\n    throw new IllegalStateException(\"MySQL not reachable at \" + mysqlHost + \":\" + mysqlPort, e);\n}\n// Also: prefer canal.socketChannel=bio (default) unless you specifically need netty.","typeGuard":null,"tryCatchPattern":"try {\n    SocketChannel ch = SocketChannelPool.open(address);\n} catch (IOException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"can't create socket\")) {\n        // netty registered no channel — fall back to BIO or surface a clear error\n        log.error(\"netty open failed; verify MySQL accept path / max_connections / handler errors\", e);\n        throw e;\n    }\n    throw e;\n}","preventionTips":["Default to canal.socketChannel=bio for clearer socket errors unless netty is required.","Verify MySQL max_connections and max_user_connections are not exhausted.","Pre-test connectivity from the canal host with the mysql CLI using the same credentials.","Watch for BusinessHandler.exceptionCaught / channelInactive lines in the log — they precede this failure."],"tags":["network","netty","connection","socket","pool","mysql"],"backgroundTag":null,"analyzedSha":"87be50e87686a3e8af08c368d0e1ffd1f59eb04a","analyzedAt":"2026-08-14T04:30:11.918Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}