diff --git a/cbits/hs_zk.c b/cbits/hs_zk.c
--- a/cbits/hs_zk.c
+++ b/cbits/hs_zk.c
@@ -1,17 +1,31 @@
 #include "hs_zk.h"
 
 // ----------------------------------------------------------------------------
+// Helpers
+// ----------------------------------------------------------------------------
+
+const stat_t* dup_stat(const stat_t* old_stat);
+const string_vector_t* dup_string_vector(const string_vector_t* old_strings);
+const acl_vector_t* dup_acl_vector(const acl_vector_t* old_acls);
+
+// ----------------------------------------------------------------------------
 // Callback Functions
 // ----------------------------------------------------------------------------
 
-void hs_zookeeper_watcher_fn(zhandle_t* zh, int type, int state,
-                             const char* path, void* watcherCtx) {
-  hs_watcher_ctx_t* watcher_ctx = (hs_watcher_ctx_t*)watcherCtx;
-  watcher_ctx->zh = zh;
-  watcher_ctx->type = type;
-  watcher_ctx->state = state;
-  watcher_ctx->path = strdup(path);
-  hs_try_putmvar(watcher_ctx->cap, watcher_ctx->mvar);
+void hs_zookeeper_node_watcher_fn(zhandle_t* zh, int type, int state,
+                                  const char* path, void* watcherCtx) {
+  if (type != ZOO_SESSION_EVENT) {
+    hs_watcher_ctx_t* watcher_ctx = (hs_watcher_ctx_t*)watcherCtx;
+    watcher_ctx->zh = zh;
+    watcher_ctx->type = type;
+    watcher_ctx->state = state;
+    watcher_ctx->path = strdup(path);
+    hs_try_putmvar(watcher_ctx->cap, watcher_ctx->mvar);
+  } else {
+    fprintf(stderr,
+            "zoovisitor.hs_zookeeper_node_watcher_fn: ignore event type: %d\n",
+            type);
+  }
 }
 
 /**
@@ -262,7 +276,7 @@
   watcher_ctx->cap = cap;
   data_completion->mvar = mvar_f;
   data_completion->cap = cap;
-  return zoo_awget(zh, path, hs_zookeeper_watcher_fn, watcher_ctx,
+  return zoo_awget(zh, path, hs_zookeeper_node_watcher_fn, watcher_ctx,
                    hs_data_completion_fn, data_completion);
 }
 
@@ -298,7 +312,7 @@
   watcher_ctx->cap = cap;
   stat_completion->mvar = mvar_f;
   stat_completion->cap = cap;
-  return zoo_awexists(zh, path, hs_zookeeper_watcher_fn, watcher_ctx,
+  return zoo_awexists(zh, path, hs_zookeeper_node_watcher_fn, watcher_ctx,
                       hs_stat_completion_fn, stat_completion);
 }
 
@@ -319,7 +333,7 @@
   watcher_ctx->cap = cap;
   strings_completion->mvar = mvar_f;
   strings_completion->cap = cap;
-  return zoo_awget_children(zh, path, hs_zookeeper_watcher_fn, watcher_ctx,
+  return zoo_awget_children(zh, path, hs_zookeeper_node_watcher_fn, watcher_ctx,
                             hs_strings_completion_fn, strings_completion);
 }
 
@@ -340,8 +354,9 @@
   watcher_ctx->cap = cap;
   strings_stat->mvar = mvar_f;
   strings_stat->cap = cap;
-  return zoo_awget_children2(zh, path, hs_zookeeper_watcher_fn, watcher_ctx,
-                             hs_strings_stat_completion_fn, strings_stat);
+  return zoo_awget_children2(zh, path, hs_zookeeper_node_watcher_fn,
+                             watcher_ctx, hs_strings_stat_completion_fn,
+                             strings_stat);
 }
 
 int hs_zoo_aget_acl(zhandle_t* zh, const char* path, HsStablePtr mvar,
@@ -372,4 +387,50 @@
                         HsInt valoffset, HsInt valuelen, int version,
                         stat_t* stat) {
   zoo_set_op_init(op, path, value + valoffset, valuelen, version, stat);
+}
+
+// ----------------------------------------------------------------------------
+// Helpers
+
+const stat_t* dup_stat(const stat_t* old_stat) {
+  stat_t* new_stat = (stat_t*)malloc(sizeof(stat_t));
+  new_stat = memcpy(new_stat, old_stat, sizeof(stat_t));
+  return new_stat;
+}
+
+const string_vector_t* dup_string_vector(const string_vector_t* old_strings) {
+  int count = old_strings->count;
+  if (count < 0) {
+    fprintf(stderr, "dup_string_vector error: count %d\n", count);
+    return NULL;
+  }
+  string_vector_t* new_strings =
+      (string_vector_t*)malloc(sizeof(string_vector_t));
+  char** vals = malloc(count * sizeof(char*));
+  for (int i = 0; i < count; ++i) {
+    vals[i] = strdup(old_strings->data[i]);
+  }
+  new_strings->count = count;
+  new_strings->data = vals;
+  return new_strings;
+}
+
+const acl_vector_t* dup_acl_vector(const acl_vector_t* old_acls) {
+  int count = old_acls->count;
+  if (count < 0) {
+    fprintf(stderr, "dup_acl_vector error: count %d\n", count);
+    return NULL;
+  }
+  acl_t* data = (acl_t*)malloc(count * sizeof(acl_t));
+  acl_t* old_data = old_acls->data;
+  for (int i = 0; i < count; ++i) {
+    data[i].perms = old_data[i].perms;
+    data[i].id.scheme = strdup(old_data[i].id.scheme);
+    data[i].id.id = strdup(old_data[i].id.id);
+  }
+
+  acl_vector_t* new_acls = (acl_vector_t*)malloc(sizeof(acl_vector_t));
+  new_acls->count = count;
+  new_acls->data = data;
+  return new_acls;
 }
diff --git a/include/hs_zk.h b/include/hs_zk.h
--- a/include/hs_zk.h
+++ b/include/hs_zk.h
@@ -12,49 +12,6 @@
 typedef struct ACL acl_t;
 typedef struct ACL_vector acl_vector_t;
 
-const stat_t* dup_stat(const stat_t* old_stat) {
-  stat_t* new_stat = (stat_t*)malloc(sizeof(stat_t));
-  new_stat = memcpy(new_stat, old_stat, sizeof(stat_t));
-  return new_stat;
-}
-
-const string_vector_t* dup_string_vector(const string_vector_t* old_strings) {
-  int count = old_strings->count;
-  if (count < 0) {
-    fprintf(stderr, "dup_string_vector error: count %d\n", count);
-    return NULL;
-  }
-  string_vector_t* new_strings =
-      (string_vector_t*)malloc(sizeof(string_vector_t));
-  char** vals = malloc(count * sizeof(char*));
-  for (int i = 0; i < count; ++i) {
-    vals[i] = strdup(old_strings->data[i]);
-  }
-  new_strings->count = count;
-  new_strings->data = vals;
-  return new_strings;
-}
-
-const acl_vector_t* dup_acl_vector(const acl_vector_t* old_acls) {
-  int count = old_acls->count;
-  if (count < 0) {
-    fprintf(stderr, "dup_acl_vector error: count %d\n", count);
-    return NULL;
-  }
-  acl_t* data = (acl_t*)malloc(count * sizeof(acl_t));
-  acl_t* old_data = old_acls->data;
-  for (int i = 0; i < count; ++i) {
-    data[i].perms = old_data[i].perms;
-    data[i].id.scheme = strdup(old_data[i].id.scheme);
-    data[i].id.id = strdup(old_data[i].id.id);
-  }
-
-  acl_vector_t* new_acls = (acl_vector_t*)malloc(sizeof(acl_vector_t));
-  new_acls->count = count;
-  new_acls->data = data;
-  return new_acls;
-}
-
 typedef struct hs_watcher_ctx_t {
   HsStablePtr mvar;
   HsInt cap;
diff --git a/src/ZooKeeper.hs b/src/ZooKeeper.hs
--- a/src/ZooKeeper.hs
+++ b/src/ZooKeeper.hs
@@ -43,8 +43,8 @@
 import           Foreign.Ptr              (FunPtr, Ptr, freeHaskellFunPtr,
                                            nullFunPtr, nullPtr, plusPtr)
 import           GHC.Stack                (HasCallStack)
-import           Z.Data.CBytes            (CBytes)
 import qualified Z.Data.CBytes            as CBytes
+import           Z.Data.CBytes            (CBytes)
 import           Z.Data.Vector            (Bytes)
 import qualified Z.Foreign                as Z
 import qualified Z.IO.FileSystem.FilePath as ZF
@@ -66,7 +66,8 @@
   -- server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"
   -> Maybe T.WatcherFn
   -- ^ the global watcher callback function. When notifications are
-  -- triggered this function will be invoked.
+  -- triggered this function will be invoked. FIXME: Calling any zoo operations
+  -- (e.g. zooGet) will cause an infinite block.
   -> CInt
   -- ^ session expiration time in milliseconds
   -> Maybe T.ClientID
@@ -205,8 +206,10 @@
   -> CBytes
   -> (T.HsWatcherCtx -> IO ())
   -- ^ The watcher callback.
-  --
   -- A watch will be set at the server to notify the client if the node changes.
+  --
+  --  /NOTE: this works different with c client. You will only receive data
+  --  and child watches, no session watches./
   -> (T.DataCompletion -> IO ())
   -- ^ The result callback when the request completes.
   --
@@ -319,6 +322,9 @@
   -- A watch will set on the specified znode on the server. The watch will be
   -- set even if the node does not exist. This allows clients to watch for
   -- nodes to appear.
+  --
+  --  /NOTE: this works different with c client. You will only receive data
+  --  and child watches, no session watches./
   -> (Maybe T.StatCompletion -> IO ())
   -- ^ The result callback when the request completes. Nothing means
   -- the node does not exist.
@@ -386,6 +392,9 @@
   -> (T.HsWatcherCtx -> IO ())
   -- ^ The watcher callback. A watch will be set at the server to notify
   --  the client if the node changes.
+  --
+  --  /NOTE: this works different with c client. You will only receive data
+  --  and child watches, no session watches./
   -> (T.StringsCompletion -> IO ())
   -- ^ The result callback when the request completes.
   --
@@ -454,6 +463,8 @@
   -> (T.HsWatcherCtx -> IO ())
   -- ^ The watcher callback. A watch will be set at the server to notify
   --  the client if the node changes.
+  --  /NOTE: this works different with c client. You will only receive data
+  --  and child watches, no session watches./
   -> (T.StringsStatCompletion -> IO ())
   -- ^ The result callback when the request completes.
   --
@@ -654,7 +665,8 @@
   -- server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"
   -> Maybe T.WatcherFn
   -- ^ the global watcher callback function. When notifications are
-  -- triggered this function will be invoked.
+  -- triggered this function will be invoked. FIXME: Calling any zoo operations
+  -- (e.g. zooGet) will cause an infinite block.
   -> CInt
   -- ^ session expiration time in milliseconds
   -> Maybe T.ClientID
diff --git a/src/ZooKeeper/Internal/FFI.hsc b/src/ZooKeeper/Internal/FFI.hsc
--- a/src/ZooKeeper/Internal/FFI.hsc
+++ b/src/ZooKeeper/Internal/FFI.hsc
@@ -1,3 +1,4 @@
+{-# LANGUAGE CApiFFI          #-}
 {-# LANGUAGE CPP              #-}
 {-# LANGUAGE UnliftedFFITypes #-}
 
@@ -228,10 +229,10 @@
 foreign import ccall safe "zookeeper.h zoo_set_watcher"
   zoo_set_watcher :: ZHandle -> FunPtr CWatcherFn -> IO (FunPtr CWatcherFn)
 
-foreign import ccall unsafe "hs_zk.h zoo_check_op_init"
+foreign import ccall unsafe "zookeeper.h zoo_check_op_init"
   c_zoo_check_op_init :: Ptr CZooOp -> BA## Word8 -> CInt -> IO ()
 
-foreign import ccall unsafe "hs_zk.h is_unrecoverable"
+foreign import ccall unsafe "zookeeper.h is_unrecoverable"
   c_is_unrecoverable :: ZHandle -> IO CInt
 
 -------------------------------------------------------------------------------
@@ -296,4 +297,4 @@
       case rc1 of
         CZOK -> f1 =<< Right <$> peekData1 data1'
         _    -> f1 $ Left rc1
-{-# INLINE withZKAsync2 #-}
+{-# INLINABLE withZKAsync2 #-}
diff --git a/zoovisitor.cabal b/zoovisitor.cabal
--- a/zoovisitor.cabal
+++ b/zoovisitor.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.2
 name:               zoovisitor
-version:            0.2.0.0
+version:            0.2.1.0
 synopsis:
   A haskell binding to Apache Zookeeper C library(mt) using Haskell Z project.
 
