{"record":{"id":"40c7c67f47e24730","repo":"alibaba/canal","slug":"zk-client-has-already-been-started","errorCode":null,"errorMessage":"zk client has already been started","messagePattern":"zk client has already been started","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"common/src/main/java/com/alibaba/otter/canal/common/zookeeper/ZooKeeperx.java","lineNumber":66,"sourceCode":"    }\n\n    public ZooKeeperx(String zkServers, int sessionTimeOut){\n        super(zkServers, sessionTimeOut);\n        _serversList = Arrays.asList(StringUtils.split(this.getServers(), SERVER_COMMA));\n        _sessionTimeOut = sessionTimeOut;\n    }\n\n    @Override\n    public void connect(Watcher watcher) {\n        ReflectionUtils.makeAccessible(zookeeperLockField);\n        ReflectionUtils.makeAccessible(zookeeperFiled);\n        Lock _zookeeperLock = (ReentrantLock) ReflectionUtils.getField(zookeeperLockField, this);\n        ZooKeeper _zk = (ZooKeeper) ReflectionUtils.getField(zookeeperFiled, this);\n\n        _zookeeperLock.lock();\n        try {\n            if (_zk != null) {\n                throw new IllegalStateException(\"zk client has already been started\");\n            }\n            String zkServers = _serversList.get(0);\n\n            try {\n                logger.debug(\"Creating new ZookKeeper instance to connect to \" + zkServers + \".\");\n                _zk = new ZooKeeper(zkServers, _sessionTimeOut, watcher);\n                configMutliCluster(_zk);\n                ReflectionUtils.setField(zookeeperFiled, this, _zk);\n            } catch (IOException e) {\n                throw new ZkException(\"Unable to connect to \" + zkServers, e);\n            }\n        } finally {\n            _zookeeperLock.unlock();\n        }\n    }\n\n    // ===============================\n","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/alibaba/canal/blob/87be50e87686a3e8af08c368d0e1ffd1f59eb04a/common/src/main/java/com/alibaba/otter/canal/common/zookeeper/ZooKeeperx.java#L48-L84","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","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."],"exampleFix":"// before\nzkClient.connect(watcher);   // second call -> IllegalStateException\n// after\nif (zkClient != null) zkClient.close();\nzkClient = new ZkClient(new ZooKeeperx(servers), sessionTimeout);\nzkClient.connect(watcher);","handlingStrategy":"validation","validationCode":"// Avoid double-connect by checking connection state first\nimport org.I0Itec.zkclient.ZkClient;\n\nvoid safeConnect(ZkClient zkClient, org.apache.zookeeper.Watcher watcher) {\n    if (zkClient != null && zkClient.getConnection() != null\n        && zkClient.getConnection().getZookeeper() != null) {\n        // already connected; do not call connect() again\n        return;\n    }\n    // ... establish connection once\n}","typeGuard":null,"tryCatchPattern":"try {\n    zkClient.connect(watcher);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"already been started\")) {\n        // already connected; treat as no-op or recreate the client\n    } else {\n        throw e;\n    }\n}","preventionTips":["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."],"tags":["zookeeper","lifecycle","connection-management"],"backgroundTag":null,"analyzedSha":"87be50e87686a3e8af08c368d0e1ffd1f59eb04a","analyzedAt":"2026-08-14T04:30:11.918Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}