alibaba/canal · critical · IOException

can't create socket!

Error message

can't create socket!

What it means

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().

Source

Thrown at driver/src/main/java/com/alibaba/otter/canal/parse/driver/mysql/socket/NettySocketChannelPool.java:66

                @Override
                protected void initChannel(Channel ch) throws Exception {
                    ch.pipeline().addLast(new BusinessHandler());// 命令过滤和handler添加管理
                }
            });
    }

    public static SocketChannel open(SocketAddress address) throws Exception {
        SocketChannel socket = null;
        ChannelFuture future = boot.connect(address).sync();

        if (future.isSuccess()) {
            future.channel().pipeline().get(BusinessHandler.class).latch.await();
            socket = chManager.get(future.channel());
        }

        if (null == socket) {
            throw new IOException("can't create socket!");
        }

        return socket;
    }

    public static class BusinessHandler extends SimpleChannelInboundHandler<ByteBuf> {

        private NettySocketChannel   socket = null;
        private final CountDownLatch latch  = new CountDownLatch(1);

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            socket.setChannel(null);
            chManager.remove(ctx.channel());// 移除
            super.channelInactive(ctx);
        }

        @Override

View on GitHub (pinned to 87be50e876)

Solutions

  1. 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.
  2. Check MySQL max_connections and the account's max_user_connections — exhaustion causes immediate post-connect drops.
  3. 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).
  4. 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).
  5. 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.

Example fix

# before: netty socket channel
 canal.socketChannel = netty

# after: fall back to bio (default) which exposes the real socket error
# (remove the line or set explicitly)
canal.socketChannel = bio
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate reachability before asking canal to open the netty socket
try (java.net.Socket s = new java.net.Socket()) {
    s.connect(new InetSocketAddress(mysqlHost, mysqlPort), 3000);
} catch (IOException e) {
    throw new IllegalStateException("MySQL not reachable at " + mysqlHost + ":" + mysqlPort, e);
}
// Also: prefer canal.socketChannel=bio (default) unless you specifically need netty.

Try / catch

try {
    SocketChannel ch = SocketChannelPool.open(address);
} catch (IOException e) {
    if (e.getMessage() != null && e.getMessage().contains("can't create socket")) {
        // netty registered no channel — fall back to BIO or surface a clear error
        log.error("netty open failed; verify MySQL accept path / max_connections / handler errors", e);
        throw e;
    }
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14). Data as JSON: /api/errors/fa421c13a7ccd20a. Report an issue: GitHub.