   #_ (errors/user-friendly-error e))
 
 (defn print-worker-out [x verbose]
-  (locking build-log/stdout-lock
-    (binding [*out* *err*]
-      (case (:type x)
-        :build-log
-        (when verbose
-          (println (build-log/event-text (:event x))))
-
-        :build-configure
-        (println (format "[%s] Configuring build." (:build-id x)))
-
-        :build-start
-        (print-build-start x)
-
-        :build-failure
-        (print-build-failure x)
-
-        :build-complete
-        (print-build-complete x)
-
-        :build-shutdown
-        (println "Build shutdown.")
-
-        :repl/action
-        (let [warnings (get-in x [:action :warnings])]
-          (print-warnings warnings))
-
-        ;; should have been handled somewhere else
-        :repl/result
-        :ignored
-
-        :repl/error
-        :ignored ;; also handled by out
-
-        :repl/runtime-connect
-        (println "JS runtime connected.")
-
-        :repl/runtime-disconnect
-        (println "JS runtime disconnected.")
-
-        :worker-shutdown
-        (println "Worker shutdown.")
-
-        :println
-        (println (:msg x))
-
-        ;; handled in dedicated channel as they should not mix with build output
-        :repl/out
-        :ignored
-
-        :repl/err
-        :ignored
-
-        ;; default
-        (prn [:log x])))))
+  (try
+
+    (locking build-log/stdout-lock
+      (binding [*out* *err*]
+        (case (:type x)
+          :build-log
+          (when verbose
+            (println (build-log/event-text (:event x))))
+
+          :build-configure
+          (println (format "[%s] Configuring build." (:build-id x)))
+
+          :build-start
+          (print-build-start x)
+
+          :build-failure
+          (print-build-failure x)
+
+          :build-complete
+          (print-build-complete x)
+
+          :build-shutdown
+          (println "Build shutdown.")
+
+          :repl/action
+          (let [warnings (get-in x [:action :warnings])]
+            (print-warnings warnings))
+
+          ;; should have been handled somewhere else
+          :repl/result
+          :ignored
+
+          :repl/error
+          :ignored ;; also handled by out
+
+          :repl/runtime-connect
+          (println "JS runtime connected.")
+
+          :repl/runtime-disconnect
+          (println "JS runtime disconnected.")
+
+          :worker-shutdown
+          (println "Worker shutdown.")
+
+          :println
+          (println (:msg x))
+
+          ;; handled in dedicated channel as they should not mix with build output
+          :repl/out
+          :ignored
+
+          :repl/err
+          :ignored
+
+          ;; default
+          (prn [:log x]))))
+
+    true
+    (catch SocketException e
+      ;; was printing to a socket that disconnected.
+      ;; no need to continue printing
+      false)
+    (catch Exception e
+      (log/debug-ex e ::stdout-ex {:x x})
+      true)))
 
 (defn stdout-dump [verbose]
   (let [chan

    [shadow.cljs.devtools.server.runtime :as runtime]
    [clojure.set :as set]
    [clojure.core.async.impl.protocols :as async-prot])
  (:import (java.io Writer InputStreamReader BufferedReader IOException ByteArrayOutputStream ByteArrayInputStream)))

(defn chan? [x]
  (satisfies? async-prot/ReadPort x))

    (async/go
      (loop []
        (when-some [x (<! chan)]
          (when (print-worker-out x verbose)
            (recur)))))

    chan))

(defmacro thread
  "same as core async thread but does not preserve bindings"
