diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java
index a0a68aa43b5429eb2a76a023a9a1e07f0d8da2a9..2d4d7e0dd06f13eca77b71032beb39c9f4b2b491 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java
@@ -77,6 +77,16 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
 
     private volatile ThreadPoolExecutor _executor;
 
+    /** this is ONLY for shared clients */
+    private static I2PSocketManager socketManager;
+
+    /**
+     *  Only destroy and replace a static shared client socket manager if it's been connected before
+     *  @since 0.9.20
+     */
+    private enum SocketManagerState { INIT, CONNECTED }
+    private static SocketManagerState _socketManagerState = SocketManagerState.INIT;
+
     public static final String PROP_USE_SSL = I2PTunnelServer.PROP_USE_SSL;
 
     /**
@@ -239,10 +249,6 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
         connectManager();
     }
 
-    /** this is ONLY for shared clients */
-    private static I2PSocketManager socketManager;
-
-
     /**
      * This is ONLY for shared clients.
      * As of 0.9.20 this is fast, and does NOT connect the manager to the router.
@@ -283,12 +289,13 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
         Log _log = tunnel.getContext().logManager().getLog(I2PTunnelClientBase.class);
         if (socketManager != null && !socketManager.isDestroyed()) {
             I2PSession s = socketManager.getSession();
-            if (s.isClosed()) {
+            if (s.isClosed() && _socketManagerState != SocketManagerState.INIT) {
                 if (_log.shouldLog(Log.INFO))
                     _log.info(tunnel.getClientOptions().getProperty("inbound.nickname") + ": Building a new socket manager since the old one closed [s=" + s + "]");
                 tunnel.removeSession(s);
                 // make sure the old one is closed
                 socketManager.destroySocketManager();
+                _socketManagerState = SocketManagerState.INIT;
                 // We could be here a LONG time, holding the lock
                 socketManager = buildSocketManager(tunnel, pkf);
             } else {
@@ -424,6 +431,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
         while (sockMgr.getSession().isClosed()) {
             try {
                 sockMgr.getSession().connect();
+                synchronized(I2PTunnelClientBase.class) {
+                    if (sockMgr == socketManager)
+                        _socketManagerState = SocketManagerState.CONNECTED;
+                }
             } catch (I2PSessionException ise) {
                 // shadows instance _log
                 Log _log = getTunnel().getContext().logManager().getLog(I2PTunnelClientBase.class);
diff --git a/core/java/src/net/i2p/client/I2PSession.java b/core/java/src/net/i2p/client/I2PSession.java
index da67e2c02c0bf9601736f84c3abf353155cb6e2f..a6ce4122ee712af166dbcf90aeacd7489930f79a 100644
--- a/core/java/src/net/i2p/client/I2PSession.java
+++ b/core/java/src/net/i2p/client/I2PSession.java
@@ -257,7 +257,7 @@ public interface I2PSession {
     /** 
      * Have we closed the session? 
      *
-     * @return true if the session is closed
+     * @return true if the session is closed, OR connect() has not been called yet
      */
     public boolean isClosed();
     
diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java
index cd6154c96ad5928eddd19b2a01ab755b14e4c731..7527dd8862e04b860a327a434ef06e57379e12a9 100644
--- a/core/java/src/net/i2p/client/I2PSessionImpl.java
+++ b/core/java/src/net/i2p/client/I2PSessionImpl.java
@@ -135,7 +135,7 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
         CLOSED
     }
 
-    private State _state = State.INIT;
+    protected State _state = State.INIT;
     protected final Object _stateLock = new Object();
 
     /** 
@@ -614,7 +614,12 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
      * Report abuse with regards to the given messageId
      */
     public void reportAbuse(int msgId, int severity) throws I2PSessionException {
-        if (isClosed()) throw new I2PSessionException(getPrefix() + "Already closed");
+        synchronized (_stateLock) {
+            if (_state == State.CLOSED)
+                throw new I2PSessionException("Already closed");
+            if (_state == State.INIT)
+                throw new I2PSessionException("Not open, must call connect() first");
+        }
         _producer.reportAbuse(this, msgId, severity);
     }
 
diff --git a/core/java/src/net/i2p/client/I2PSessionImpl2.java b/core/java/src/net/i2p/client/I2PSessionImpl2.java
index 9f8a5465d31184b14239f1dc49726688b9b2b39a..5cd2b930c4295776e164815acce960a22f6ae9d4 100644
--- a/core/java/src/net/i2p/client/I2PSessionImpl2.java
+++ b/core/java/src/net/i2p/client/I2PSessionImpl2.java
@@ -247,7 +247,12 @@ class I2PSessionImpl2 extends I2PSessionImpl {
     public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expires)
                    throws I2PSessionException {
         if (_log.shouldLog(Log.DEBUG)) _log.debug("sending message");
-        if (isClosed()) throw new I2PSessionException("Already closed");
+        synchronized (_stateLock) {
+            if (_state == State.CLOSED)
+                throw new I2PSessionException("Already closed");
+            if (_state == State.INIT)
+                throw new I2PSessionException("Not open, must call connect() first");
+        }
         updateActivity();
 
         // Sadly there is no way to send something completely uncompressed in a backward-compatible way,
diff --git a/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java b/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java
index 48ca6016e8c963ee400cc7f12d08bfd2fce09d7c..0185496aa3aceea641ed8b587274bcdb5b68edd2 100644
--- a/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java
+++ b/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java
@@ -256,7 +256,12 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 {
      * @since 0.9.14
      */
     private byte[] prepPayload(byte[] payload, int offset, int size, int proto, int fromPort, int toPort) throws I2PSessionException {
-        if (isClosed()) throw new I2PSessionException("Already closed");
+        synchronized (_stateLock) {
+            if (_state == State.CLOSED)
+                throw new I2PSessionException("Already closed");
+            if (_state == State.INIT)
+                throw new I2PSessionException("Not open, must call connect() first");
+        }
         updateActivity();
 
         if (shouldCompress(size))
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index 0cd02d33d12e8a5cbd5776e89fe01dcb65d8b49b..5fd579e05b82a079d03afc83109a625c6e423097 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -18,7 +18,7 @@ public class RouterVersion {
     /** deprecated */
     public final static String ID = "Monotone";
     public final static String VERSION = CoreVersion.VERSION;
-    public final static long BUILD = 29;
+    public final static long BUILD = 30;
 
     /** for example "-test" */
     public final static String EXTRA = "-rc";