diff --git a/ipfshttpclient/exceptions.py b/ipfshttpclient/exceptions.py
index 22d03c2..02b773e 100644
--- a/ipfshttpclient/exceptions.py
+++ b/ipfshttpclient/exceptions.py
@@ -10,13 +10,14 @@ The class hierarchy for exceptions is:
 	 │    ├── EncoderMissingError
 	 │    ├── EncodingError
 	 │    └── DecodingError
-	 └── CommunicationError
-	      ├── ProtocolError
-	      ├── StatusError
-	      ├── ErrorResponse
-	      │    └── PartialErrorResponse
-	      ├── ConnectionError
-	      └── TimeoutError
+	 ├── CommunicationError
+	 │    ├── ProtocolError
+	 │    ├── StatusError
+	 │    ├── ErrorResponse
+	 │    │    └── PartialErrorResponse
+	 │    ├── ConnectionError
+	 │    └── TimeoutError
+	 └── MatcherSpecInvalidError
 
 """
 import typing as ty
@@ -33,25 +34,27 @@ class AddressError(Error, multiaddr.exceptions.Error):  # type: ignore[no-any-un
 	"""Raised when the provided daemon location Multiaddr does not match any
 	of the supported patterns."""
 	__slots__ = ("addr",)
-	#addr: ty.Union[str, bytes]
+
+	addr: ty.Union[str, bytes]
 	
 	def __init__(self, addr: ty.Union[str, bytes]) -> None:
-		self.addr = addr  # type: ty.Union[str, bytes]
+		self.addr = addr
 		Error.__init__(self, "Unsupported Multiaddr pattern: {0!r}".format(addr))
 
 
 class VersionMismatch(Warning):
 	"""Raised when daemon version is not supported by this client version."""
 	__slots__ = ("current", "minimum", "maximum")
-	#current: ty.Sequence[int]
-	#minimum: ty.Sequence[int]
-	#maximum: ty.Sequence[int]
+
+	current: ty.Sequence[int]
+	minimum: ty.Sequence[int]
+	maximum: ty.Sequence[int]
 	
 	def __init__(self, current: ty.Sequence[int], minimum: ty.Sequence[int],
 	             maximum: ty.Sequence[int]) -> None:
-		self.current = current  # type: ty.Sequence[int]
-		self.minimum = minimum  # type: ty.Sequence[int]
-		self.maximum = maximum  # type: ty.Sequence[int]
+		self.current = current
+		self.minimum = minimum
+		self.maximum = maximum
 		
 		msg = "Unsupported daemon version '{}' (not in range: {} ≤ … < {})".format(
 			".".join(map(str, current)), ".".join(map(str, minimum)), ".".join(map(str, maximum))
@@ -65,10 +68,11 @@ class VersionMismatch(Warning):
 class EncoderError(Error):
 	"""Base class for all encoding and decoding related errors."""
 	__slots__ = ("encoder_name",)
-	#encoder_name: str
+
+	encoder_name: str
 	
 	def __init__(self, message: str, encoder_name: str) -> None:
-		self.encoder_name = encoder_name  # type: str
+		self.encoder_name = encoder_name
 		
 		super().__init__(message)
 
@@ -85,10 +89,11 @@ class EncodingError(EncoderError):
 	"""Raised when encoding a Python object into a byte string has failed
 	due to some problem with the input data."""
 	__slots__ = ("original",)
-	#original: Exception
+
+	original: Exception
 	
 	def __init__(self, encoder_name: str, original: Exception) -> None:
-		self.original = original  # type: Exception
+		self.original = original
 		
 		super().__init__("Object encoding error: {}".format(original), encoder_name)
 
@@ -97,27 +102,44 @@ class DecodingError(EncoderError):
 	"""Raised when decoding a byte string to a Python object has failed due to
 	some problem with the input data."""
 	__slots__ = ("original",)
-	#original: Exception
+
+	original: Exception
 	
 	def __init__(self, encoder_name: str, original: Exception) -> None:
-		self.original = original  # type: Exception
+		self.original = original
 		
 		super().__init__("Object decoding error: {}".format(original), encoder_name)
 
 
+##################
+# filescanner.py #
+##################
+
+class MatcherSpecInvalidError(Error, TypeError):
+	"""
+	An attempt was made to build a matcher using matcher_from_spec, but an invalid
+	specification was provided.
+	"""
+
+	def __init__(self, invalid_spec: ty.Any) -> None:
+		super().__init__(
+			f"Don't know how to create a Matcher from spec {invalid_spec!r}"
+		)
+
+
 ###########
 # http.py #
 ###########
 class CommunicationError(Error):
 	"""Base class for all network communication related errors."""
 	__slots__ = ("original",)
-	#original: ty.Optional[Exception]
+
+	original: ty.Optional[Exception]
 	
 	def __init__(self, original: ty.Optional[Exception],
 	             _message: ty.Optional[str] = None) -> None:
-		self.original = original  # type: ty.Optional[Exception]
+		self.original = original
 		
-		msg = ""  # type: str
 		if _message:
 			msg = _message
 		else:
diff --git a/ipfshttpclient/filescanner.py b/ipfshttpclient/filescanner.py
index ddbe8bc..c0a4483 100644
--- a/ipfshttpclient/filescanner.py
+++ b/ipfshttpclient/filescanner.py
@@ -16,6 +16,8 @@ from . import utils
 # To encourage contributions from PyCharm users, we redefine AnyStr.
 #
 # This will get inlined if/when PyCharm no longer flags typing.AnyStr.
+from .exceptions import MatcherSpecInvalidError
+
 AnyStr = ty.TypeVar('AnyStr', bytes, str)
 
 if sys.version_info >= (3, 7):  #PY37+
@@ -413,13 +415,6 @@ match_spec_t = ty.Union[
 ]
 
 
-class MatcherSpecInvalidError(TypeError):
-	def __init__(self, invalid_spec: ty.Any) -> None:
-		super().__init__(
-			f"Don't know how to create a {Matcher.__name__} from spec {invalid_spec!r}"
-		)
-
-
 def _require_spec(spec: ty.Optional[match_spec_t[AnyStr]]) -> match_spec_t[AnyStr]:
 	"""
 	Assist the type checker by narrowing the number of places accepting Optional.

diff --git a/test/unit/test_exceptions.py b/test/unit/test_exceptions.py
new file mode 100644
index 0000000..ec6ac63
--- /dev/null
+++ b/test/unit/test_exceptions.py
@@ -0,0 +1,18 @@
+
+from ipfshttpclient.exceptions import MatcherSpecInvalidError, Error
+from ipfshttpclient.filescanner import Matcher
+
+
+def test_matcher_spec_invalid_error_message():
+	ex = MatcherSpecInvalidError('junk')
+	assert ex.args[0] == f"Don't know how to create a {Matcher.__name__} from spec 'junk'"
+
+
+def test_matcher_spec_invalid_error_multiple_inheritance():
+	ex = MatcherSpecInvalidError('wrong')
+
+	# Base class of all exceptions in this library
+	assert isinstance(ex, Error)
+
+	# Base class of type errors
+	assert isinstance(ex, TypeError)
diff --git a/test/unit/test_filescanner.py b/test/unit/test_filescanner.py
index 716b091..27cb03f 100644
--- a/test/unit/test_filescanner.py
+++ b/test/unit/test_filescanner.py
@@ -6,8 +6,10 @@ import typing as ty
 import pytest
 
 from datetime import datetime
+
 from ipfshttpclient import filescanner
 
+from ipfshttpclient.exceptions import MatcherSpecInvalidError
 from ipfshttpclient.filescanner import FSNodeEntry
 from ipfshttpclient.filescanner import FSNodeType
 
@@ -164,7 +166,7 @@ def test_glob_matching(
 
 @pytest.mark.parametrize('spec', [123, datetime.now()])
 def test_matcher_from_spec_rejects_invalid_spec_type(spec: ty.Any) -> None:
-	with pytest.raises(filescanner.MatcherSpecInvalidError):
+	with pytest.raises(MatcherSpecInvalidError):
 		filescanner.matcher_from_spec(spec)
 
 

Move MatcherSpecInvalidError into `ipfshttpclient/exceptions.py`
https://github.com/ipfs-shipyard/py-ipfs-http-client/blob/09cae7672a39ca3ff991684c1b31cfe7875174c3/ipfshttpclient/filescanner.py#L416-L420

We currently expose all possible exceptions in the `ipfshttpclient/exceptions.py` module. Ideally this one should be moved there to and added into the existing exception hierarchy (inheriting from both some exception there and `TypeError`, I think).
test/unit/test_exceptions.py::test_matcher_spec_invalid_error_message test/unit/test_exceptions.py::test_matcher_spec_invalid_error_multiple_inheritance test/unit/test_filescanner.py::test_fs_node_entry_as_repr test/unit/test_filescanner.py::test_fs_node_entry_as_str test/unit/test_filescanner.py::test_glob_compile[literal-expected0-kwargs0] test/unit/test_filescanner.py::test_glob_compile[literal-expected1-kwargs1] test/unit/test_filescanner.py::test_glob_compile[*.a-expected2-kwargs2] test/unit/test_filescanner.py::test_glob_compile[*.a-expected3-kwargs3] test/unit/test_filescanner.py::test_glob_compile[*/**/*.dir/**/**/.hidden-expected4-kwargs4] test/unit/test_filescanner.py::test_glob_compile[*/**/*.dir/**/**/.hidden-expected5-kwargs5] test/unit/test_filescanner.py::test_glob_compile[././/////////./*.a-expected6-kwargs6] test/unit/test_filescanner.py::test_glob_compile[././/////////./*.a-expected7-kwargs7] test/unit/test_filescanner.py::test_glob_compile[*/*.a-expected8-kwargs8] test/unit/test_filescanner.py::test_glob_compile[*/*.a-expected9-kwargs9] test/unit/test_filescanner.py::test_glob_sep_normalize test/unit/test_filescanner.py::test_glob_errors[../*0] test/unit/test_filescanner.py::test_glob_errors[../*1] test/unit/test_filescanner.py::test_glob_errors[/absolute/file/path0] test/unit/test_filescanner.py::test_glob_errors[/absolute/file/path1] test/unit/test_filescanner.py::test_glob_not_implemented test/unit/test_filescanner.py::test_glob_matching[literal-other-False-False-False-kwargs0] test/unit/test_filescanner.py::test_glob_matching[literal-literal-False-False-True-kwargs1] test/unit/test_filescanner.py::test_glob_matching[literal-literal/more-False-False-False-kwargs2] test/unit/test_filescanner.py::test_glob_matching[literal-other-False-False-False-kwargs3] test/unit/test_filescanner.py::test_glob_matching[literal-literal-False-False-True-kwargs4] test/unit/test_filescanner.py::test_glob_matching[literal-literal/more-False-False-False-kwargs5] test/unit/test_filescanner.py::test_glob_matching[literal/more-other-False-False-False-kwargs6] test/unit/test_filescanner.py::test_glob_matching[literal/more-literal-False-True-False-kwargs7] test/unit/test_filescanner.py::test_glob_matching[literal/more-literal-True-True-True-kwargs8] test/unit/test_filescanner.py::test_glob_matching[literal/more-literal/more-False-False-True-kwargs9] test/unit/test_filescanner.py::test_glob_matching[literal/more-other-False-False-False-kwargs10] test/unit/test_filescanner.py::test_glob_matching[literal/more-literal-False-True-False-kwargs11] test/unit/test_filescanner.py::test_glob_matching[literal/more-literal-True-True-True-kwargs12] test/unit/test_filescanner.py::test_glob_matching[literal/more-literal/more-False-False-True-kwargs13] test/unit/test_filescanner.py::test_glob_matching[literal/more-other-False-False-False-kwargs14] test/unit/test_filescanner.py::test_glob_matching[literal/more-literal-False-False-False-kwargs15] test/unit/test_filescanner.py::test_glob_matching[literal/more-literal-True-False-True-kwargs16] test/unit/test_filescanner.py::test_glob_matching[literal/more-literal/more-False-False-False-kwargs17] test/unit/test_filescanner.py::test_glob_matching[*.a-.a-False-False-False-kwargs18] test/unit/test_filescanner.py::test_glob_matching[*.a-.a-False-False-True-kwargs19] test/unit/test_filescanner.py::test_glob_matching[*.a-.a-True-False-False-kwargs20] test/unit/test_filescanner.py::test_glob_matching[*.a-.a-True-False-True-kwargs21] test/unit/test_filescanner.py::test_glob_matching[*.a/-.a-False-False-False-kwargs22] test/unit/test_filescanner.py::test_glob_matching[*.a/-.a-False-False-False-kwargs23] test/unit/test_filescanner.py::test_glob_matching[*.a/-.a-True-False-False-kwargs24] test/unit/test_filescanner.py::test_glob_matching[*.a/-.a-True-False-True-kwargs25] test/unit/test_filescanner.py::test_glob_matching[*/**/*.dir/**/**/.hidden-.dir/.hidden-False-False-False-kwargs26] test/unit/test_filescanner.py::test_glob_matching[*/**/*.dir/**/**/.hidden-a/.dir/.hidden-False-True-False-kwargs27] test/unit/test_filescanner.py::test_glob_matching[*/**/*.dir/**/**/.hidden-a/b.dir/.hidden-False-True-True-kwargs28] test/unit/test_filescanner.py::test_glob_matching[*/**/*.dir/**/**/.hidden-a/u/v/w/b.dir/c/d/e/f/.hidden-False-True-True-kwargs29] test/unit/test_filescanner.py::test_glob_matching[**-.a-False-True-False-kwargs30] test/unit/test_filescanner.py::test_glob_matching[pattern31-.a-False-True-False-kwargs31] test/unit/test_filescanner.py::test_glob_matching[[^/\\\\]+[/\\\\](IMG-\\d{0,4}\\.jpeg)?$-Camera/IMG-0169.jpeg-False-True-True-kwargs32] test/unit/test_filescanner.py::test_glob_matching[[^/\\\\]+[/\\\\](IMG-\\d{0,4}\\.jpeg)?$-Camera-True-True-True-kwargs33] test/unit/test_filescanner.py::test_glob_matching[[^/\\\\]+[/\\\\](IMG-\\d{0,4}\\.jpeg)?$-Camera/Thumbs.db-False-True-False-kwargs34] test/unit/test_filescanner.py::test_glob_matching[[^/\\]+[/\\](IMG-\d{0,4}\.jpeg)?$-Camera/IMG-0169.jpeg-False-True-True-kwargs35] test/unit/test_filescanner.py::test_glob_matching[[^/\\]+[/\\](IMG-\d{0,4}\.jpeg)?$-Camera-True-True-True-kwargs36] test/unit/test_filescanner.py::test_glob_matching[[^/\\]+[/\\](IMG-\d{0,4}\.jpeg)?$-Camera/Thumbs.db-False-True-False-kwargs37] test/unit/test_filescanner.py::test_glob_matching[pattern38-Camera/Thumbs.db-False-True-False-kwargs38] test/unit/test_filescanner.py::test_glob_matching[pattern39-Camera/IMG-1279.jpeg-False-True-True-kwargs39] test/unit/test_filescanner.py::test_glob_matching[pattern40-Camera/IMG-1279.jpeg-False-True-True-kwargs40] test/unit/test_filescanner.py::test_glob_matching[pattern41-a/.dir/.hidden-False-True-False-kwargs41] test/unit/test_filescanner.py::test_glob_matching[pattern42-a/.dir/.hidden-False-True-False-kwargs42] test/unit/test_filescanner.py::test_glob_matching[pattern43-a/b.dir/.hidden-False-True-True-kwargs43] test/unit/test_filescanner.py::test_glob_matching[pattern44-a/b.dir/.hidden-False-True-True-kwargs44] test/unit/test_filescanner.py::test_glob_matching[pattern45-???-False-False-False-kwargs45] test/unit/test_filescanner.py::test_glob_matching[pattern46-???-False-False-False-kwargs46] test/unit/test_filescanner.py::test_matcher_from_spec_rejects_invalid_spec_type[123] test/unit/test_filescanner.py::test_matcher_from_spec_rejects_invalid_spec_type[spec1] test/unit/test_filescanner.py::test_matcher_from_spec_builds_recursive_glob_matcher test/unit/test_filescanner.py::test_matcher_from_spec_builds_recursive_open_matcher test/unit/test_filescanner.py::test_matcher_from_spec_builds_non_recursive_glob_matcher test/unit/test_filescanner.py::test_matcher_from_spec_builds_non_recursive_open_matcher test/unit/test_filescanner.py::test_walk_fd_unsupported test/unit/test_filescanner.py::test_walk_instaclose test/unit/test_filescanner.py::test_walk[/root/data/temp_dir/tmptwocvj55/ipfs-shipyard__py-ipfs-http-client__0.0/test/unit/../functional/fake_dir_almost_empty/-None-expected0] test/unit/test_filescanner.py::test_walk[/root/data/temp_dir/tmptwocvj55/ipfs-shipyard__py-ipfs-http-client__0.0/test/unit/../functional/fake_dir-pattern1-expected1] test/unit/test_filescanner.py::test_supports_fd