alibaba/canal · error · IllegalStateException
zk client has already been started
Error message
zk client has already been started
What it means
Thrown by ZooKeeperx.connect when the underlying ZkConnection._zk field is already non-null, meaning connect() was called twice on the same ZooKeeperx instance without an intervening disconnect/close. The guard protects against leaking/duplicating ZooKeeper client connections, which would otherwise create duplicate watchers and session churn.
Source
Thrown at common/src/main/java/com/alibaba/otter/canal/common/zookeeper/ZooKeeperx.java:66
}
public ZooKeeperx(String zkServers, int sessionTimeOut){
super(zkServers, sessionTimeOut);
_serversList = Arrays.asList(StringUtils.split(this.getServers(), SERVER_COMMA));
_sessionTimeOut = sessionTimeOut;
}
@Override
public void connect(Watcher watcher) {
ReflectionUtils.makeAccessible(zookeeperLockField);
ReflectionUtils.makeAccessible(zookeeperFiled);
Lock _zookeeperLock = (ReentrantLock) ReflectionUtils.getField(zookeeperLockField, this);
ZooKeeper _zk = (ZooKeeper) ReflectionUtils.getField(zookeeperFiled, this);
_zookeeperLock.lock();
try {
if (_zk != null) {
throw new IllegalStateException("zk client has already been started");
}
String zkServers = _serversList.get(0);
try {
logger.debug("Creating new ZookKeeper instance to connect to " + zkServers + ".");
_zk = new ZooKeeper(zkServers, _sessionTimeOut, watcher);
configMutliCluster(_zk);
ReflectionUtils.setField(zookeeperFiled, this, _zk);
} catch (IOException e) {
throw new ZkException("Unable to connect to " + zkServers, e);
}
} finally {
_zookeeperLock.unlock();
}
}
// ===============================
View on GitHub (pinned to 87be50e876)
Solutions
- Call zkClient.close() (or disconnect()) before re-invoking connect() on the same instance.
- Create a fresh ZooKeeperx/ZkClient instance for each connect cycle rather than reusing a connected one.
- Guard the connect call with a state check or ensure connect() is invoked exactly once in the client lifecycle.
Example fix
// before zkClient.connect(watcher); // second call -> IllegalStateException // after if (zkClient != null) zkClient.close(); zkClient = new ZkClient(new ZooKeeperx(servers), sessionTimeout); zkClient.connect(watcher);
Defensive patterns
Strategy: validation
Validate before calling
// Avoid double-connect by checking connection state first
import org.I0Itec.zkclient.ZkClient;
void safeConnect(ZkClient zkClient, org.apache.zookeeper.Watcher watcher) {
if (zkClient != null && zkClient.getConnection() != null
&& zkClient.getConnection().getZookeeper() != null) {
// already connected; do not call connect() again
return;
}
// ... establish connection once
} Try / catch
try {
zkClient.connect(watcher);
} catch (IllegalStateException e) {
if (e.getMessage().contains("already been started")) {
// already connected; treat as no-op or recreate the client
} else {
throw e;
}
} Prevention
- Treat connect() as a once-per-instance lifecycle call.
- Always pair reconnect logic with a prior close()/disconnect().
- Reuse a single shared ZkClient bean rather than re-connecting per request.
When it happens
Trigger: Calling zkClient.connect(watcher) more than once on the same org.I0Itec.zkclient.ZkClient backed by a ZooKeeperx; reconnect logic that re-invokes connect() after a transient failure without first calling close()/disconnect().
Common situations: Custom HA/reconnect wrappers around ZkClient that call connect() on recovery; re-initializing a shared ZkClient bean in a Spring context refresh; test code that reuses a ZkClient across test methods without teardown.
Related errors
- {} has startup , don't repeat start
- {} isn't start , please check
- Unable to connect to {}
- zookeeper_create_error, serveraddrs={}
- Disconnect pulsar consumer error
AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14).
Data as JSON: /api/errors/40c7c67f47e24730.
Report an issue: GitHub.