diff --git a/pyproject.toml b/pyproject.toml
index 2b13956..d625342 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -54,7 +54,7 @@ dependencies = [
     "six>=1.9.0",
     "psutil",
     "packaging",
-    "testgres.os_ops>=1.0.0,<2.0.0",
+    "testgres.os_ops @ git+https://github.com/postgrespro/testgres.os_ops.git@2.0.0",
 ]
 
 [project.urls]
diff --git a/src/exceptions.py b/src/exceptions.py
index b4aad64..dafd842 100644
--- a/src/exceptions.py
+++ b/src/exceptions.py
@@ -1,6 +1,7 @@
 # coding: utf-8
 
 import six
+import typing
 
 from testgres.operations.exceptions import TestgresException
 from testgres.operations.exceptions import ExecUtilException
@@ -13,22 +14,71 @@ class PortForException(TestgresException):
 
 @six.python_2_unicode_compatible
 class QueryException(TestgresException):
-    def __init__(self, message=None, query=None):
-        super(QueryException, self).__init__(message)
+    _description: typing.Optional[str]
+    _query: typing.Optional[str]
 
-        self.message = message
-        self.query = query
+    def __init__(
+        self,
+        message: typing.Optional[str] = None,
+        query: typing.Optional[str] = None
+    ):
+        assert message is None or type(message) == str  # noqa: E721
+        assert query is None or type(query) == str  # noqa: E721
+
+        super().__init__(message)
+
+        self._description = message
+        self._query = query
+        return
+
+    @property
+    def message(self) -> str:
+        assert self._description is None or type(self._description) == str  # noqa: E721
+        assert self._query is None or type(self._query) == str  # noqa: E721
 
-    def __str__(self):
         msg = []
 
-        if self.message:
-            msg.append(self.message)
+        if self._description:
+            msg.append(self._description)
 
-        if self.query:
-            msg.append(u'Query: {}'.format(self.query))
+        if self._query:
+            msg.append(u'Query: {}'.format(self._query))
 
-        return six.text_type('\n').join(msg)
+        r = six.text_type('\n').join(msg)
+        assert type(r) == str  # noqa: E721
+        return r
+
+    @property
+    def description(self) -> typing.Optional[str]:
+        assert self._description is None or type(self._description) == str  # noqa: E721
+        return self._description
+
+    @property
+    def query(self) -> typing.Optional[str]:
+        assert self._query is None or type(self._query) == str  # noqa: E721
+        return self._query
+
+    def __repr__(self) -> str:
+        assert type(self) == QueryException  # noqa: E721
+        assert __class__ == QueryException  # noqa: E721
+
+        args = []
+
+        if self._description is not None:
+            args.append(("message", self._description))
+
+        if self._query is not None:
+            args.append(("query", self._query))
+
+        result = "{}(".format(__class__.__name__)
+        sep = ""
+        for a in args:
+            if a[1] is not None:
+                result += sep + a[0] + "=" + repr(a[1])
+                sep = ", "
+            continue
+        result += ")"
+        return result
 
 
 class TimeoutException(QueryException):
@@ -41,23 +91,72 @@ class CatchUpException(QueryException):
 
 @six.python_2_unicode_compatible
 class StartNodeException(TestgresException):
-    def __init__(self, message=None, files=None):
-        super(StartNodeException, self).__init__(message)
+    _description: typing.Optional[str]
+    _files: typing.Optional[typing.Iterable]
+
+    def __init__(
+        self,
+        message: typing.Optional[str] = None,
+        files: typing.Optional[typing.Iterable] = None
+    ):
+        assert message is None or type(message) == str  # noqa: E721
+        assert files is None or isinstance(files, typing.Iterable)
 
-        self.message = message
-        self.files = files
+        super().__init__(message)
+
+        self._description = message
+        self._files = files
+        return
+
+    @property
+    def message(self) -> str:
+        assert self._description is None or type(self._description) == str  # noqa: E721
+        assert self._files is None or isinstance(self._files, typing.Iterable)
 
-    def __str__(self):
         msg = []
 
-        if self.message:
-            msg.append(self.message)
+        if self._description:
+            msg.append(self._description)
 
-        for f, lines in self.files or []:
+        for f, lines in self._files or []:
+            assert type(f) == str  # noqa: E721
+            assert type(lines) in [str, bytes]  # noqa: E721
             msg.append(u'{}\n----\n{}\n'.format(f, lines))
 
         return six.text_type('\n').join(msg)
 
+    @property
+    def description(self) -> typing.Optional[str]:
+        assert self._description is None or type(self._description) == str  # noqa: E721
+        return self._description
+
+    @property
+    def files(self) -> typing.Optional[typing.Iterable]:
+        assert self._files is None or isinstance(self._files, typing.Iterable)
+        return self._files
+
+    def __repr__(self) -> str:
+        assert type(self) == StartNodeException  # noqa: E721
+        assert __class__ == StartNodeException  # noqa: E721
+
+        args = []
+
+        if self._description is not None:
+            args.append(("message", self._description))
+
+        if self._files is not None:
+            args.append(("files", self._files))
+
+        result = "{}(".format(__class__.__name__)
+        sep = ""
+        for a in args:
+            if a[1] is not None:
+                result += sep + a[0] + "=" + repr(a[1])
+                sep = ", "
+            continue
+        result += ")"
+        return result
+
 
 class InitNodeException(TestgresException):
     pass
diff --git a/src/node.py b/src/node.py
index b651c87..2eedeb4 100644
--- a/src/node.py
+++ b/src/node.py
@@ -713,7 +713,7 @@ def _maybe_stop_logger(self):
         if self._logger:
             self._logger.stop()
 
-    def _collect_special_files(self):
+    def _collect_special_files(self) -> typing.List[typing.Tuple[str, bytes]]:
         result = []
 
         # list of important files + last N lines
diff --git a/tests/requirements.txt b/tests/requirements.txt
index d8eb1f8..8343d21 100644
--- a/tests/requirements.txt
+++ b/tests/requirements.txt
@@ -3,4 +3,4 @@ pytest
 pytest-xdist
 psycopg2
 six
-testgres.os_ops>=1.0.0,<2.0.0
+git+https://github.com/postgrespro/testgres.os_ops.git@2.0.0
diff --git a/tests/test_os_ops_remote.py b/tests/test_os_ops_remote.py
index 8be98da..86cd665 100755
--- a/tests/test_os_ops_remote.py
+++ b/tests/test_os_ops_remote.py
@@ -33,7 +33,8 @@ def test_rmdirs__try_to_delete_file(self, os_ops: OsOperations):
 
         assert os.path.exists(path)
         assert type(x.value) == ExecUtilException   # noqa: E721
-        assert x.value.message == "Utility exited with non-zero code (20). Error: `cannot remove '" + path + "': it is not a directory`"
+        assert x.value.description == "Utility exited with non-zero code (20). Error: `cannot remove '" + path + "': it is not a directory`"
+        assert x.value.message.startswith(x.value.description)
         assert type(x.value.error) == str  # noqa: E721
         assert x.value.error.strip() == "cannot remove '" + path + "': it is not a directory"
         assert type(x.value.exit_code) == int  # noqa: E721
diff --git a/tests/test_testgres_common.py b/tests/test_testgres_common.py
index 69d07b0..16d7b6a 100644
--- a/tests/test_testgres_common.py
+++ b/tests/test_testgres_common.py
@@ -219,9 +219,11 @@ def test_double_start(self, node_svc: PostgresNodeService):
 
             assert x is not None
             assert type(x.value) == StartNodeException  # noqa: E721
+            assert type(x.value.description) == str  # noqa: E721
             assert type(x.value.message) == str  # noqa: E721
 
-            assert x.value.message == "Cannot start node"
+            assert x.value.description == "Cannot start node"
+            assert x.value.message.startswith(x.value.description)
 
             assert node.is_started
 
diff --git a/tests/units/__init__.py b/tests/units/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/units/exceptions/QueryException/__init__.py b/tests/units/exceptions/QueryException/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/units/exceptions/QueryException/test_set001__constructor.py b/tests/units/exceptions/QueryException/test_set001__constructor.py
new file mode 100644
index 0000000..05ff0be
--- /dev/null
+++ b/tests/units/exceptions/QueryException/test_set001__constructor.py
@@ -0,0 +1,52 @@
+from src.exceptions import QueryException
+from src.exceptions import TestgresException as testgres__TestgresException
+
+
+class TestSet001_Constructor:
+    def test_001__default(self):
+        e = QueryException()
+        assert type(e) == QueryException  # noqa: E721
+        assert isinstance(e, testgres__TestgresException)
+        assert e.source is None
+        assert e.message == ""
+        assert e.description is None
+        assert e.query is None
+        assert str(e) == ""
+        assert repr(e) == "QueryException()"
+        return
+
+    def test_002__message(self):
+        e = QueryException(message="abc\n123")
+        assert type(e) == QueryException  # noqa: E721
+        assert isinstance(e, testgres__TestgresException)
+        assert e.source is None
+        assert e.message == "abc\n123"
+        assert e.description == "abc\n123"
+        assert e.query is None
+        assert str(e) == "abc\n123"
+        assert repr(e) == "QueryException(message='abc\\n123')"
+        return
+
+    def test_003__query(self):
+        e = QueryException(query="cba\n321")
+        assert type(e) == QueryException  # noqa: E721
+        assert isinstance(e, testgres__TestgresException)
+        assert e.source is None
+        assert e.message == "Query: cba\n321"
+        assert e.description is None
+        assert e.query == "cba\n321"
+        assert str(e) == "Query: cba\n321"
+        assert repr(e) == "QueryException(query='cba\\n321')"
+        return
+
+    def test_004__all(self):
+        e = QueryException(message="mmm", query="cba\n321")
+        assert type(e) == QueryException  # noqa: E721
+        assert isinstance(e, testgres__TestgresException)
+        assert e.source is None
+        assert e.message == "mmm\nQuery: cba\n321"
+        assert e.description == "mmm"
+        assert e.query == "cba\n321"
+        assert str(e) == "mmm\nQuery: cba\n321"
+        assert repr(e) == "QueryException(message='mmm', query='cba\\n321')"
+        return
diff --git a/tests/units/exceptions/StartNodeException/__init__.py b/tests/units/exceptions/StartNodeException/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/units/exceptions/StartNodeException/test_set001__constructor.py b/tests/units/exceptions/StartNodeException/test_set001__constructor.py
new file mode 100644
index 0000000..9d7f762
--- /dev/null
+++ b/tests/units/exceptions/StartNodeException/test_set001__constructor.py
@@ -0,0 +1,52 @@
+from src.exceptions import StartNodeException
+from src.exceptions import TestgresException as testgres__TestgresException
+
+
+class TestSet001_Constructor:
+    def test_001__default(self):
+        e = StartNodeException()
+        assert type(e) == StartNodeException  # noqa: E721
+        assert isinstance(e, testgres__TestgresException)
+        assert e.source is None
+        assert e.message == ""
+        assert e.description is None
+        assert e.files is None
+        assert str(e) == ""
+        assert repr(e) == "StartNodeException()"
+        return
+
+    def test_002__message(self):
+        e = StartNodeException(message="abc\n123")
+        assert type(e) == StartNodeException  # noqa: E721
+        assert isinstance(e, testgres__TestgresException)
+        assert e.source is None
+        assert e.message == "abc\n123"
+        assert e.description == "abc\n123"
+        assert e.files is None
+        assert str(e) == "abc\n123"
+        assert repr(e) == "StartNodeException(message='abc\\n123')"
+        return
+
+    def test_003__files(self):
+        e = StartNodeException(files=[("f\n1", b'line1\nline2')])
+        assert type(e) == StartNodeException  # noqa: E721
+        assert isinstance(e, testgres__TestgresException)
+        assert e.source is None
+        assert e.message == "f\n1\n----\nb'line1\\nline2'\n"
+        assert e.description is None
+        assert e.files == [("f\n1", b'line1\nline2')]
+        assert str(e) == "f\n1\n----\nb'line1\\nline2'\n"
+        assert repr(e) == "StartNodeException(files=[('f\\n1', b'line1\\nline2')])"
+        return
+
+    def test_004__all(self):
+        e = StartNodeException(message="mmm", files=[("f\n1", b'line1\nline2')])
+        assert type(e) == StartNodeException  # noqa: E721
+        assert isinstance(e, testgres__TestgresException)
+        assert e.source is None
+        assert e.message == "mmm\nf\n1\n----\nb'line1\\nline2'\n"
+        assert e.description == "mmm"
+        assert e.files == [("f\n1", b'line1\nline2')]
+        assert str(e) == "mmm\nf\n1\n----\nb'line1\\nline2'\n"
+        assert repr(e) == "StartNodeException(message='mmm', files=[('f\\n1', b'line1\\nline2')])"
+        return
diff --git a/tests/units/exceptions/__init__.py b/tests/units/exceptions/__init__.py
new file mode 100644
index 0000000..e69de29
