rokob: It turns out that copy/deepcopy go through the pickle machinery via __reduce__ and __reduce_ex__, so things that are not copyable throw TypeErrors from pickling.
diff --git a/rollbar/lib/__init__.py b/rollbar/lib/__init__.py
index 1afaee1..9a3f664 100644
--- a/rollbar/lib/__init__.py
+++ b/rollbar/lib/__init__.py
@@ -173,7 +173,10 @@ def dict_merge(a, b):
         if k in result and isinstance(result[k], dict):
             result[k] = dict_merge(result[k], v)
         else:
-            result[k] = copy.deepcopy(v)
+            try:
+                result[k] = copy.deepcopy(v)
+            except:
+                result[k] = '<Uncopyable obj:(%s)>' % (v,)
 
     return result
 

Exception while reporting exc_info to Rollbar. TypeError("can't pickle select.epoll objects",)
I'm using `rollbar==0.13.16` with `tornado==4.5.2` and getting the following exception:

```
[E 171025 05:47:48 __init__:411] Exception while reporting message to Rollbar. TypeError("can't pickle select.epoll objects",)
    Traceback (most recent call last):
      File "/usr/local/lib/python3.6/site-packages/tornado/web.py", line 1511, in _execute
        result = yield result
      File "/usr/local/lib/python3.6/site-packages/tornado/gen.py", line 1055, in run
        value = future.result()
      File "/usr/local/lib/python3.6/site-packages/tornado/concurrent.py", line 238, in result
        raise_exc_info(self._exc_info)
      File "<string>", line 4, in raise_exc_info
      File "/usr/local/lib/python3.6/site-packages/tornado/gen.py", line 1063, in run
        yielded = self.gen.throw(*exc_info)
      File "/project-ps-hsm/src/handlers/balance.py", line 21, in get
        yield self.check_blacklist()
      File "/usr/local/lib/python3.6/site-packages/tornado/gen.py", line 1055, in run
        value = future.result()
      File "/usr/local/lib/python3.6/site-packages/tornado/concurrent.py", line 238, in result
        raise_exc_info(self._exc_info)
      File "<string>", line 4, in raise_exc_info
      File "/usr/local/lib/python3.6/site-packages/tornado/gen.py", line 1063, in run
        yielded = self.gen.throw(*exc_info)
      File "/project-ps-hsm/src/handlers/_base.py", line 81, in check_blacklist
        reason = yield is_blacklisted(self.can, self.current_user)
      File "/usr/local/lib/python3.6/site-packages/tornado/gen.py", line 1055, in run
        value = future.result()
      File "/usr/local/lib/python3.6/site-packages/tornado/concurrent.py", line 238, in result
        raise_exc_info(self._exc_info)
      File "<string>", line 4, in raise_exc_info
      File "/usr/local/lib/python3.6/site-packages/tornado/gen.py", line 1063, in run
        yielded = self.gen.throw(*exc_info)
      File "/project-ps-hsm/src/utils/afp_processing.py", line 42, in is_blacklisted
        'SECRET': config.INTERNAL_API_SECRET['customer'],
      File "/usr/local/lib/python3.6/site-packages/tornado/gen.py", line 1055, in run
        value = future.result()
      File "/usr/local/lib/python3.6/site-packages/tornado/concurrent.py", line 238, in result
        raise_exc_info(self._exc_info)
      File "<string>", line 4, in raise_exc_info
    tornado.httpclient.HTTPError: HTTP 500: Internal Server Error
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "/usr/local/lib/python3.6/site-packages/rollbar/__init__.py", line 409, in report_message
        return _report_message(message, level, request, extra_data, payload_data)
      File "/usr/local/lib/python3.6/site-packages/rollbar/__init__.py", line 712, in _report_message
        data = dict_merge(data, payload_data)
      File "/usr/local/lib/python3.6/site-packages/rollbar/lib/__init__.py", line 174, in dict_merge
        result[k] = dict_merge(result[k], v)
      File "/usr/local/lib/python3.6/site-packages/rollbar/lib/__init__.py", line 176, in dict_merge
        result[k] = copy.deepcopy(v)
      File "/usr/local/lib/python3.6/copy.py", line 150, in deepcopy
        y = copier(x, memo)
      File "/usr/local/lib/python3.6/copy.py", line 215, in _deepcopy_list
        append(deepcopy(a, memo))
      File "/usr/local/lib/python3.6/copy.py", line 150, in deepcopy
        y = copier(x, memo)
      File "/usr/local/lib/python3.6/copy.py", line 240, in _deepcopy_dict
        y[deepcopy(key, memo)] = deepcopy(value, memo)
      File "/usr/local/lib/python3.6/copy.py", line 150, in deepcopy
        y = copier(x, memo)
      File "/usr/local/lib/python3.6/copy.py", line 220, in _deepcopy_tuple
        y = [deepcopy(a, memo) for a in x]
      File "/usr/local/lib/python3.6/copy.py", line 220, in <listcomp>
        y = [deepcopy(a, memo) for a in x]
      File "/usr/local/lib/python3.6/copy.py", line 180, in deepcopy
        y = _reconstruct(x, memo, *rv)
      File "/usr/local/lib/python3.6/copy.py", line 280, in _reconstruct
        state = deepcopy(state, memo)
      File "/usr/local/lib/python3.6/copy.py", line 150, in deepcopy
        y = copier(x, memo)
      File "/usr/local/lib/python3.6/copy.py", line 240, in _deepcopy_dict
        y[deepcopy(key, memo)] = deepcopy(value, memo)
      File "/usr/local/lib/python3.6/copy.py", line 180, in deepcopy
        y = _reconstruct(x, memo, *rv)
      File "/usr/local/lib/python3.6/copy.py", line 280, in _reconstruct
        state = deepcopy(state, memo)
      File "/usr/local/lib/python3.6/copy.py", line 150, in deepcopy
        y = copier(x, memo)
      File "/usr/local/lib/python3.6/copy.py", line 240, in _deepcopy_dict
        y[deepcopy(key, memo)] = deepcopy(value, memo)
      File "/usr/local/lib/python3.6/copy.py", line 180, in deepcopy
        y = _reconstruct(x, memo, *rv)
      File "/usr/local/lib/python3.6/copy.py", line 280, in _reconstruct
        state = deepcopy(state, memo)
      File "/usr/local/lib/python3.6/copy.py", line 150, in deepcopy
        y = copier(x, memo)
      File "/usr/local/lib/python3.6/copy.py", line 240, in _deepcopy_dict
        y[deepcopy(key, memo)] = deepcopy(value, memo)
      File "/usr/local/lib/python3.6/copy.py", line 180, in deepcopy
        y = _reconstruct(x, memo, *rv)
      File "/usr/local/lib/python3.6/copy.py", line 280, in _reconstruct
        state = deepcopy(state, memo)
      File "/usr/local/lib/python3.6/copy.py", line 150, in deepcopy
        y = copier(x, memo)
      File "/usr/local/lib/python3.6/copy.py", line 240, in _deepcopy_dict
        y[deepcopy(key, memo)] = deepcopy(value, memo)
      File "/usr/local/lib/python3.6/copy.py", line 169, in deepcopy
        rv = reductor(4)
    TypeError: can't pickle select.epoll objects
```

It results in certain errors don't show up in Rollbar which is pretty bad 😞 
It seems like not every `exc_info` is pickable and this situation should be handled properly. I suggest still reporting the error message, even without the exception info.
diff --git a/rollbar/test/test_lib.py b/rollbar/test/test_lib.py
new file mode 100644
index 0000000..201a2ed
--- /dev/null
+++ b/rollbar/test/test_lib.py
@@ -0,0 +1,59 @@
+from rollbar.lib import dict_merge
+
+from rollbar.test import BaseTest
+
+class RollbarLibTest(BaseTest):
+    def test_dict_merge_not_dict(self):
+        a = {'a': {'b': 42}}
+        b = 99
+        result = dict_merge(a, b)
+
+        self.assertEqual(99, result)
+
+    def test_dict_merge_dicts_independent(self):
+        a = {'a': {'b': 42}}
+        b = {'x': {'y': 99}}
+        result = dict_merge(a, b)
+
+        self.assertIn('a', result)
+        self.assertIn('b', result['a'])
+        self.assertEqual(42, result['a']['b'])
+        self.assertIn('x', result)
+        self.assertIn('y', result['x'])
+        self.assertEqual(99, result['x']['y'])
+
+    def test_dict_merge_dicts(self):
+        a = {'a': {'b': 42}}
+        b = {'a': {'c': 99}}
+        result = dict_merge(a, b)
+
+        self.assertIn('a', result)
+        self.assertIn('b', result['a'])
+        self.assertIn('c', result['a'])
+        self.assertEqual(42, result['a']['b'])
+        self.assertEqual(99, result['a']['c'])
+
+    def test_dict_merge_dicts_second_wins(self):
+        a = {'a': {'b': 42}}
+        b = {'a': {'b': 99}}
+        result = dict_merge(a, b)
+
+        self.assertIn('a', result)
+        self.assertIn('b', result['a'])
+        self.assertEqual(99, result['a']['b'])
+
+    def test_dict_merge_dicts_select_poll(self):
+        import select
+        poll = getattr(select, 'poll', None)
+        if poll is None:
+            return
+        p = poll()
+        a = {'a': {'b': 42}}
+        b = {'a': {'y': p}}
+        result = dict_merge(a, b)
+
+        self.assertIn('a', result)
+        self.assertIn('b', result['a'])
+        self.assertEqual(42, result['a']['b'])
+        self.assertIn('y', result['a'])
+        self.assertRegex(result['a']['y'], r'Uncopyable obj')

pip install -e .
parse_log_pytest
mock webob blinker unittest2 pytest
apt-get update apt-get install -y gcc
3.4
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
blinker==1.5
certifi==2021.5.30
charset-normalizer==2.0.12
idna==3.10
importlib-metadata==4.8.3
iniconfig==1.1.1
linecache2==1.0.0
mock==5.2.0
packaging==21.3
pluggy==1.0.0
py==1.11.0
pyparsing==3.1.4
pytest==7.0.1
requests==2.27.1
-e git+https://github.com/rollbar/pyrollbar.git@c9d34b1d1544415a17d5a79e90179a763e739bfc#egg=rollbar
six==1.17.0
tomli==1.2.3
traceback2==1.4.0
typing_extensions==4.1.1
unittest2==1.1.0
urllib3==1.26.20
WebOb==1.8.9
zipp==3.6.0

name: pyrollbar
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
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - 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=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - argparse==1.4.0
      - attrs==22.2.0
      - blinker==1.5
      - charset-normalizer==2.0.12
      - idna==3.10
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - linecache2==1.0.0
      - mock==5.2.0
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - requests==2.27.1
      - six==1.17.0
      - tomli==1.2.3
      - traceback2==1.4.0
      - typing-extensions==4.1.1
      - unittest2==1.1.0
      - urllib3==1.26.20
      - webob==1.8.9
      - zipp==3.6.0
prefix: /opt/conda/envs/pyrollbar

rollbar/test/test_lib.py::RollbarLibTest::test_dict_merge_dicts_select_poll
rollbar/test/test_lib.py::RollbarLibTest::test_dict_merge_dicts rollbar/test/test_lib.py::RollbarLibTest::test_dict_merge_dicts_independent rollbar/test/test_lib.py::RollbarLibTest::test_dict_merge_dicts_second_wins rollbar/test/test_lib.py::RollbarLibTest::test_dict_merge_not_dict
rollbar/lib/__init__.py
rollbar/lib/__init__.py