diff --git a/src/color_ssh/__init__.py b/src/color_ssh/__init__.py
index 8ce9b36..7525d19 100644
--- a/src/color_ssh/__init__.py
+++ b/src/color_ssh/__init__.py
@@ -1,1 +1,1 @@
-__version__ = '0.1.3'
+__version__ = '0.1.4'
diff --git a/src/color_ssh/color_cat.py b/src/color_ssh/color_cat.py
index 036ced8..7509d49 100644
--- a/src/color_ssh/color_cat.py
+++ b/src/color_ssh/color_cat.py
@@ -75,7 +75,7 @@ def main(argv=sys.argv, stdin=io2bytes(sys.stdin), stdout=io2bytes(sys.stdout),
             fh = stdin if path is None else io.open(path, 'rb', 0)
             try:
                 for line in iter(fh.readline, b''):
-                    stdout.write(setting.prefix + line + RESET)
+                    stdout.write(setting.prefix + line.rstrip(b'\n') + RESET + b'\n')
                     stdout.flush()
             finally:
                 if fh is not stdin:
diff --git a/src/color_ssh/color_ssh.py b/src/color_ssh/color_ssh.py
index 6cfbb2d..5aaac08 100644
--- a/src/color_ssh/color_ssh.py
+++ b/src/color_ssh/color_ssh.py
@@ -131,7 +131,7 @@ class Setting(object):
     def _build_host_string(user, host):
         ret = host
         if user:
-            ret = '%s@' % user + ret
+            ret = str('%s@') % user + ret
         return ret
 
     @staticmethod
diff --git a/src/color_ssh/util/util.py b/src/color_ssh/util/util.py
index 8bdea63..ed7e846 100644
--- a/src/color_ssh/util/util.py
+++ b/src/color_ssh/util/util.py
@@ -2,6 +2,7 @@ from __future__ import division, print_function, absolute_import, unicode_litera
 
 import sys
 import os
+import errno
 
 __all__ = ['PY3', 'arg2bytes', 'io2bytes', 'distribute', 'exception_handler']
 
@@ -52,6 +53,12 @@ def exception_handler(exception_func):
                 return func(*args, **kwargs)
             except KeyboardInterrupt:
                 return 130
+            except IOError as e:
+                if e.errno == errno.EPIPE:
+                    return 0
+                else:
+                    exception_func(e)
+                    return 1
             except Exception as e:
                 exception_func(e)
                 return 1

Reset color before newline
diff --git a/tests/color_ssh/test_color_cat.py b/tests/color_ssh/test_color_cat.py
index 390d0b0..38d50cc 100644
--- a/tests/color_ssh/test_color_cat.py
+++ b/tests/color_ssh/test_color_cat.py
@@ -110,8 +110,8 @@ class TestMain(TestCase):
             ret = color_cat.main(args, stdout=out, stderr=err)
             self.assertEqual(ret, 0)
         self.assertEqual(out.getvalue(),
-                         b'\x1b[31mfoo\n\x1b[0m\x1b[31mbar\n\x1b[0m\x1b[31mbaz\n\x1b[0m'
-                         b'\x1b[31m123\n\x1b[0m\x1b[31m456\n\x1b[0m\x1b[31m789\n\x1b[0m')
+                         b'\x1b[31mfoo\x1b[0m\n\x1b[31mbar\x1b[0m\n\x1b[31mbaz\x1b[0m\n'
+                         b'\x1b[31m123\x1b[0m\n\x1b[31m456\x1b[0m\n\x1b[31m789\x1b[0m\n')
         self.assertEqual(err.getvalue(), b'')
 
     def test_main_error(self):
diff --git a/tests/color_ssh/test_color_ssh.py b/tests/color_ssh/test_color_ssh.py
index 524e613..d035868 100644
--- a/tests/color_ssh/test_color_ssh.py
+++ b/tests/color_ssh/test_color_ssh.py
@@ -98,9 +98,10 @@ class TestSetting(TestCase):
         ])
 
         # upload
-        self._check(self._parse([
+        result = self._parse([
             '-H', 'server-11 root@server-12', '--distribute', 'echo "foo bar"', '--upload', 'dir1/x', 'dir1/y', 'z'
-        ]), [
+        ])
+        self._check(result, [
             ('server-11', ['ssh', 'server-11', 'echo', 'foo bar', 'dir1/x', 'dir1/y'], [
                 ['ssh', 'server-11', 'mkdir', '-p', 'dir1'],
                 ['scp', 'dir1/x', 'server-11:dir1/x'],
@@ -109,6 +110,10 @@ class TestSetting(TestCase):
             ('server-12', ['ssh', 'root@server-12', 'echo', 'foo bar', 'z'],
              [['scp', 'z', 'root@server-12:z']]),
         ])
+        for _, cmd, setup in result.tasks:
+            self.assertTrue(all(isinstance(c, str) for c in cmd))
+            for xs in setup:
+                self.assertTrue(all(isinstance(c, str) for c in xs))
 
     def test_parse_args_error(self):
         with self.withBytesOutput() as (out, err):
@@ -132,10 +137,10 @@ class TestMain(TestCase):
     def test_main_single_proc(self):
         # requires: POSIX environment, color-cat command
         def f(bs):
-            return b'\x1b[7m\x1b[35mtests/resources/test_color_ssh_01.sh\x1b[0m|\x1b[0m\x1b[35m' + bs + b'\n\x1b[0m'
+            return b'\x1b[7m\x1b[35mtests/resources/test_color_ssh_01.sh\x1b[0m|\x1b[0m\x1b[35m' + bs + b'\x1b[0m\n'
 
         def g(bs):
-            return b'\x1b[7m\x1b[35mtests/resources/test_color_ssh_01.sh\x1b[0m+\x1b[0m\x1b[35m' + bs + b'\n\x1b[0m'
+            return b'\x1b[7m\x1b[35mtests/resources/test_color_ssh_01.sh\x1b[0m+\x1b[0m\x1b[35m' + bs + b'\x1b[0m\n'
 
         with self.__with_temp_output() as (out, err):
             args = ['color-ssh', '--ssh', str('bash'),
@@ -152,10 +157,10 @@ class TestMain(TestCase):
     def test_main_multi_proc(self):
         # requires: POSIX environment, color-cat command
         def f(bs):
-            return b'\x1b[7m\x1b[35mtests/resources/test_color_ssh_01.sh\x1b[0m|\x1b[0m\x1b[35m' + bs + b'\n\x1b[0m'
+            return b'\x1b[7m\x1b[35mtests/resources/test_color_ssh_01.sh\x1b[0m|\x1b[0m\x1b[35m' + bs + b'\x1b[0m\n'
 
         def g(bs):
-            return b'\x1b[7m\x1b[35mtests/resources/test_color_ssh_01.sh\x1b[0m+\x1b[0m\x1b[35m' + bs + b'\n\x1b[0m'
+            return b'\x1b[7m\x1b[35mtests/resources/test_color_ssh_01.sh\x1b[0m+\x1b[0m\x1b[35m' + bs + b'\x1b[0m\n'
 
         with self.__with_temp_output() as (out, err):
             path = os.path.join('tests', 'resources', 'test_color_ssh_01.sh')
@@ -199,6 +204,18 @@ class TestMain(TestCase):
             self.assertEqual(out.read(), b'')
             self.assertTrue(b'No such file or directory' in err.read())
 
+    def test_run_task_error(self):
+        with self.__with_temp_output() as (out, err):
+            ret = color_ssh.run_task(('lab', 'echo x', ['true', 'false']))
+            self.assertEqual(ret, 1)
+
+            out.seek(0)
+            err.seek(0)
+
+            self.assertEqual(out.read(), b'')
+            self.assertEqual(b'RuntimeError: Failed to execute setup command: false\nlabel=lab, command=echo x\n',
+                             err.read())
+
     @staticmethod
     @contextmanager
     def __with_temp_output():
diff --git a/tests/color_ssh/util/test_util.py b/tests/color_ssh/util/test_util.py
index 03f9fc3..9eb76ff 100644
--- a/tests/color_ssh/util/test_util.py
+++ b/tests/color_ssh/util/test_util.py
@@ -12,6 +12,12 @@ class TestUtil(TestCase):
 
         self.assertEqual(f(), 130)
 
+        @exception_handler(lambda e: e)
+        def g():
+            raise IOError(32, '')
+
+        self.assertEqual(g(), 0)
+
     def test_distribute(self):
         self.assertEqual(distribute(0, []), [])
         self.assertEqual(distribute(0, ['a']), [])

pip install -e .
parse_log_pytest
requirements.txt
pytest pytest-cov six mog-commons>=0.2.2
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
-e git+https://github.com/mogproject/color-ssh.git@4c8d1949137759046ddff3b922eb84148c6ffa19#egg=color_ssh
coverage==7.8.0
exceptiongroup==1.2.2
iniconfig==2.1.0
Jinja2==3.1.6
MarkupSafe==3.0.2
mog-commons==0.2.3
packaging==24.2
pluggy==1.5.0
pytest==8.3.5
pytest-cov==6.0.0
six==1.17.0
tomli==2.2.1

name: color-ssh
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - coverage==7.8.0
      - exceptiongroup==1.2.2
      - iniconfig==2.1.0
      - jinja2==3.1.6
      - markupsafe==3.0.2
      - mog-commons==0.2.3
      - packaging==24.2
      - pluggy==1.5.0
      - pytest==8.3.5
      - pytest-cov==6.0.0
      - six==1.17.0
      - tomli==2.2.1
prefix: /opt/conda/envs/color-ssh

tests/color_ssh/test_color_cat.py::TestMain::test_main tests/color_ssh/test_color_ssh.py::TestMain::test_main_single_proc tests/color_ssh/util/test_util.py::TestUtil::test_exception_handler
tests/color_ssh/test_color_cat.py::TestSetting::test_parse_args_error
tests/color_ssh/test_color_cat.py::TestSetting::test_parse_args tests/color_ssh/test_color_cat.py::TestMain::test_main_error tests/color_ssh/test_color_ssh.py::TestSetting::test_parse_args tests/color_ssh/test_color_ssh.py::TestSetting::test_parse_args_error tests/color_ssh/test_color_ssh.py::TestSetting::test_parse_host_error tests/color_ssh/test_color_ssh.py::TestMain::test_main_load_error tests/color_ssh/test_color_ssh.py::TestMain::test_main_multi_proc tests/color_ssh/test_color_ssh.py::TestMain::test_main_task_error tests/color_ssh/test_color_ssh.py::TestMain::test_run_task_error tests/color_ssh/util/test_util.py::TestUtil::test_distribute tests/color_ssh/util/test_util.py::TestUtil::test_distribute_error
src/color_ssh/util/util.py src/color_ssh/color_ssh.py src/color_ssh/color_cat.py src/color_ssh/__init__.py
src/color_ssh/util/util.py src/color_ssh/color_ssh.py src/color_ssh/color_cat.py src/color_ssh/__init__.py