diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 6eb6bd7..db08cdd 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -67,7 +67,7 @@ jobs:
         run: "check-wheel-contents dist/*.whl"
       - name: "Check long_description"
         run: "python -m twine check dist/*"
-        
+
   install-dev:
     strategy:
       matrix:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 14175b2..3850ab0 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -7,18 +7,18 @@ repos:
         language_version: python3.8
 
   - repo: https://github.com/PyCQA/isort
-    rev: 5.7.0
+    rev: 5.8.0
     hooks:
       - id: isort
         additional_dependencies: [toml]
         language_version: python3.8
 
-  - repo: https://gitlab.com/pycqa/flake8
-    rev: 3.8.4
+  - repo: https://github.com/pycqa/flake8
+    rev: 3.9.0
     hooks:
     - id: flake8
-      language_version: python3.8
       additional_dependencies: [flake8-bugbear]
+      language_version: python3.8
 
   - repo: https://github.com/pre-commit/pre-commit-hooks
     rev: v3.4.0
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index ede0a8b..f3c7262 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -7,7 +7,7 @@ Versions follow `CalVer <https://calver.org>`_ with a strict backwards compatibi
 The third digit is only for regressions.
 
 
-20.1.0 (UNRELEASED)
+21.1.0 (UNRELEASED)
 -------------------
 
 
@@ -30,7 +30,8 @@ Deprecations:
 Changes:
 ^^^^^^^^
 
-*none*
+- ``service_identity.exceptions.VerificationError`` can now be pickled and is overall more well-behaved as an exception.
+  This raises the requirement of ``attrs`` to 19.1.0.
 
 
 ----
diff --git a/setup.py b/setup.py
index f5e4073..21cca45 100644
--- a/setup.py
+++ b/setup.py
@@ -34,7 +34,7 @@ CLASSIFIERS = [
     "Topic :: Software Development :: Libraries :: Python Modules",
 ]
 INSTALL_REQUIRES = [
-    "attrs>=17.4.0",
+    "attrs>=19.1.0",
     "ipaddress; python_version<'3.3'",
     "pyasn1-modules",
     # Place pyasn1 after pyasn1-modules to workaround setuptools install bug:
diff --git a/src/service_identity/__init__.py b/src/service_identity/__init__.py
index 28a9223..f9d0e89 100644
--- a/src/service_identity/__init__.py
+++ b/src/service_identity/__init__.py
@@ -12,7 +12,7 @@ from .exceptions import (
 )
 
 
-__version__ = "20.1.0.dev0"
+__version__ = "21.1.0.dev0"
 
 __title__ = "service-identity"
 __description__ = "Service identity verification for pyOpenSSL & cryptography."
diff --git a/src/service_identity/exceptions.py b/src/service_identity/exceptions.py
index 4d103d1..f80c3b3 100644
--- a/src/service_identity/exceptions.py
+++ b/src/service_identity/exceptions.py
@@ -18,7 +18,7 @@ class SubjectAltNameWarning(DeprecationWarning):
     """
 
 
-@attr.s
+@attr.s(auto_exc=True)
 class VerificationError(Exception):
     """
     Service identity verification failed.

diff --git a/tests/test_common.py b/tests/test_common.py
index 5f14c00..aa2744a 100644
--- a/tests/test_common.py
+++ b/tests/test_common.py
@@ -1,6 +1,7 @@
 from __future__ import absolute_import, division, print_function
 
 import ipaddress
+import pickle
 
 import pytest
 import six
@@ -667,12 +668,32 @@ class TestIsIPAddress(object):
 
 
 class TestVerificationError(object):
-    """
-    The __str__ returns something sane.
-    """
+    def test_repr_str(self):
+        """
+        The __str__ and __repr__ methods return something helpful.
+        """
+        try:
+            raise VerificationError(errors=["foo"])
+        except VerificationError as e:
+            assert repr(e) == str(e)
+            assert str(e) != ""
+
+    @pytest.mark.parametrize("proto", range(0, pickle.HIGHEST_PROTOCOL + 1))
+    @pytest.mark.parametrize(
+        "exc",
+        [
+            VerificationError(errors=[]),
+            VerificationError(errors=[DNSMismatch("example.com")]),
+            VerificationError([]),
+            VerificationError([DNSMismatch("example.com")]),
+        ],
+    )
+    def test_pickle(self, exc, proto):
+        """
+        Exceptions can be pickled and unpickled.
+        """
+        new_exc = pickle.loads(pickle.dumps(exc, proto))
 
-    try:
-        raise VerificationError(errors=["foo"])
-    except VerificationError as e:
-        assert repr(e) == str(e)
-        assert str(e) != ""
+        # Exceptions can't be compared.
+        assert exc.__class__ == new_exc.__class__
+        assert exc.__dict__ == new_exc.__dict__

VerificationError can't be un-pickled
Ran into this while trying to serialize some errors.

```
import pickle
from service_identity.exceptions import VerificationError
error = VerificationError(errors=[])
pickle.loads(pickle.dumps(error))
```

```
Traceback (most recent call last):
  File "foo.py", line 4, in <module>
    pickle.loads(pickle.dumps(error))
TypeError: __init__() missing 1 required positional argument: 'errors'
```

Maybe it needs auto_exc=True?  :)
tests/test_common.py::TestVerificationError::test_pickle[exc0-0] tests/test_common.py::TestVerificationError::test_pickle[exc0-1] tests/test_common.py::TestVerificationError::test_pickle[exc0-2] tests/test_common.py::TestVerificationError::test_pickle[exc0-3] tests/test_common.py::TestVerificationError::test_pickle[exc0-4] tests/test_common.py::TestVerificationError::test_pickle[exc0-5] tests/test_common.py::TestVerificationError::test_pickle[exc1-0] tests/test_common.py::TestVerificationError::test_pickle[exc1-1] tests/test_common.py::TestVerificationError::test_pickle[exc1-2] tests/test_common.py::TestVerificationError::test_pickle[exc1-3] tests/test_common.py::TestVerificationError::test_pickle[exc1-4] tests/test_common.py::TestVerificationError::test_pickle[exc1-5]
tests/test_common.py::TestVerifyServiceIdentity::test_dns_id_success tests/test_common.py::TestVerifyServiceIdentity::test_integration_dns_id_fail tests/test_common.py::TestVerifyServiceIdentity::test_ip_address_success tests/test_common.py::TestVerifyServiceIdentity::test_obligatory_missing tests/test_common.py::TestVerifyServiceIdentity::test_obligatory_mismatch tests/test_common.py::TestVerifyServiceIdentity::test_optional_missing tests/test_common.py::TestVerifyServiceIdentity::test_optional_mismatch tests/test_common.py::TestVerifyServiceIdentity::test_contains_optional_and_matches tests/test_common.py::TestContainsInstance::test_positive tests/test_common.py::TestContainsInstance::test_negative tests/test_common.py::TestDNS_ID::test_enforces_unicode tests/test_common.py::TestDNS_ID::test_handles_missing_idna tests/test_common.py::TestDNS_ID::test_ascii_works_without_idna tests/test_common.py::TestDNS_ID::test_idna_used_if_available_on_non_ascii tests/test_common.py::TestDNS_ID::test_catches_invalid_dns_ids[ tests/test_common.py::TestDNS_ID::test_catches_invalid_dns_ids[] tests/test_common.py::TestDNS_ID::test_catches_invalid_dns_ids[host,name] tests/test_common.py::TestDNS_ID::test_catches_invalid_dns_ids[192.168.0.0] tests/test_common.py::TestDNS_ID::test_catches_invalid_dns_ids[::1] tests/test_common.py::TestDNS_ID::test_catches_invalid_dns_ids[1234] tests/test_common.py::TestDNS_ID::test_lowercases tests/test_common.py::TestDNS_ID::test_verifies_only_dns tests/test_common.py::TestDNS_ID::test_simple_match tests/test_common.py::TestDNS_ID::test_simple_mismatch tests/test_common.py::TestDNS_ID::test_matches tests/test_common.py::TestDNS_ID::test_mismatches tests/test_common.py::TestURI_ID::test_enforces_unicode tests/test_common.py::TestURI_ID::test_create_DNS_ID tests/test_common.py::TestURI_ID::test_lowercases tests/test_common.py::TestURI_ID::test_catches_missing_colon tests/test_common.py::TestURI_ID::test_is_only_valid_for_uri tests/test_common.py::TestURI_ID::test_protocol_mismatch tests/test_common.py::TestURI_ID::test_dns_mismatch tests/test_common.py::TestURI_ID::test_match tests/test_common.py::TestSRV_ID::test_enforces_unicode tests/test_common.py::TestSRV_ID::test_create_DNS_ID tests/test_common.py::TestSRV_ID::test_lowercases tests/test_common.py::TestSRV_ID::test_catches_missing_dot tests/test_common.py::TestSRV_ID::test_catches_missing_underscore tests/test_common.py::TestSRV_ID::test_is_only_valid_for_SRV tests/test_common.py::TestSRV_ID::test_match tests/test_common.py::TestSRV_ID::test_match_idna tests/test_common.py::TestSRV_ID::test_mismatch_service_name tests/test_common.py::TestSRV_ID::test_mismatch_dns tests/test_common.py::TestDNSPattern::test_enforces_bytes tests/test_common.py::TestDNSPattern::test_catches_empty tests/test_common.py::TestDNSPattern::test_catches_NULL_bytes tests/test_common.py::TestDNSPattern::test_catches_ip_address tests/test_common.py::TestDNSPattern::test_invalid_wildcard tests/test_common.py::TestURIPattern::test_enforces_bytes tests/test_common.py::TestURIPattern::test_catches_missing_colon tests/test_common.py::TestURIPattern::test_catches_wildcards tests/test_common.py::TestSRVPattern::test_enforces_bytes tests/test_common.py::TestSRVPattern::test_catches_missing_underscore tests/test_common.py::TestSRVPattern::test_catches_wildcards tests/test_common.py::TestValidateDNSWildcardPattern::test_allows_only_one_wildcard tests/test_common.py::TestValidateDNSWildcardPattern::test_wildcard_must_be_left_most tests/test_common.py::TestValidateDNSWildcardPattern::test_must_have_at_least_three_parts tests/test_common.py::TestValidateDNSWildcardPattern::test_valid_patterns tests/test_common.py::TestIPAddressPattern::test_invalid_ip tests/test_common.py::TestIPAddressPattern::test_verify_equal[1.1.1.1] tests/test_common.py::TestIPAddressPattern::test_verify_equal[::1] tests/test_common.py::TestFindMatches::test_one_match tests/test_common.py::TestFindMatches::test_no_match tests/test_common.py::TestFindMatches::test_multiple_matches tests/test_common.py::TestIsIPAddress::test_ips[127.0.0.1_0] tests/test_common.py::TestIsIPAddress::test_ips[127.0.0.1_1] tests/test_common.py::TestIsIPAddress::test_ips[172.16.254.12] tests/test_common.py::TestIsIPAddress::test_ips[*.0.0.1] tests/test_common.py::TestIsIPAddress::test_ips[::1] tests/test_common.py::TestIsIPAddress::test_ips[*::1] tests/test_common.py::TestIsIPAddress::test_ips[2001:0db8:0000:0000:0000:ff00:0042:8329] tests/test_common.py::TestIsIPAddress::test_ips[2001:0db8::ff00:0042:8329] tests/test_common.py::TestIsIPAddress::test_not_ips[*.twistedmatrix.com] tests/test_common.py::TestIsIPAddress::test_not_ips[twistedmatrix.com] tests/test_common.py::TestIsIPAddress::test_not_ips[mail.google.com] tests/test_common.py::TestIsIPAddress::test_not_ips[omega7.de] tests/test_common.py::TestIsIPAddress::test_not_ips[omega7] tests/test_common.py::TestIsIPAddress::test_not_ips[127.\xff.0.1] tests/test_common.py::TestVerificationError::test_repr_str tests/test_common.py::TestVerificationError::test_pickle[exc2-0] tests/test_common.py::TestVerificationError::test_pickle[exc2-1] tests/test_common.py::TestVerificationError::test_pickle[exc2-2] tests/test_common.py::TestVerificationError::test_pickle[exc2-3] tests/test_common.py::TestVerificationError::test_pickle[exc2-4] tests/test_common.py::TestVerificationError::test_pickle[exc2-5] tests/test_common.py::TestVerificationError::test_pickle[exc3-0] tests/test_common.py::TestVerificationError::test_pickle[exc3-1] tests/test_common.py::TestVerificationError::test_pickle[exc3-2] tests/test_common.py::TestVerificationError::test_pickle[exc3-3] tests/test_common.py::TestVerificationError::test_pickle[exc3-4] tests/test_common.py::TestVerificationError::test_pickle[exc3-5]