codecov[bot]: # [Codecov](https://codecov.io/gh/getsentry/responses/pull/235?src=pr&el=h1) Report
> Merging [#235](https://codecov.io/gh/getsentry/responses/pull/235?src=pr&el=desc) into [master](https://codecov.io/gh/getsentry/responses/commit/f89de1bfa616655d6b5fa0e0d5836113357ed986?src=pr&el=desc) will **increase** coverage by `0.35%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/getsentry/responses/pull/235/graphs/tree.svg?width=650&token=x2tAGR7rxE&height=150&src=pr)](https://codecov.io/gh/getsentry/responses/pull/235?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #235      +/-   ##
==========================================
+ Coverage   93.13%   93.48%   +0.35%     
==========================================
  Files           3        3              
  Lines         918      952      +34     
==========================================
+ Hits          855      890      +35     
+ Misses         63       62       -1
```


| [Impacted Files](https://codecov.io/gh/getsentry/responses/pull/235?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [test\_responses.py](https://codecov.io/gh/getsentry/responses/pull/235/diff?src=pr&el=tree#diff-dGVzdF9yZXNwb25zZXMucHk=) | `96.79% <100%> (+0.17%)` | :arrow_up: |
| [responses.py](https://codecov.io/gh/getsentry/responses/pull/235/diff?src=pr&el=tree#diff-cmVzcG9uc2VzLnB5) | `96.65% <100%> (+0.32%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/getsentry/responses/pull/235?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/getsentry/responses/pull/235?src=pr&el=footer). Last update [f89de1b...895315d](https://codecov.io/gh/getsentry/responses/pull/235?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

diff --git a/responses.py b/responses.py
index 134939a..90bde93 100644
--- a/responses.py
+++ b/responses.py
@@ -390,6 +390,9 @@ class CallbackResponse(BaseResponse):
             raise result
 
         status, r_headers, body = result
+        if isinstance(body, Exception):
+            raise body
+
         body = _handle_body(body)
         headers.update(r_headers)
 

callback results are not unpacked prior to checking for exception bodies
This means the way to mock an error for a callback differs from the way to mock andd error for standard use. Presumably this is not desired - it is not documented at any rate.
diff --git a/test_responses.py b/test_responses.py
index f202a51..2a085f8 100644
--- a/test_responses.py
+++ b/test_responses.py
@@ -417,6 +417,46 @@ def test_callback():
     assert_reset()
 
 
+def test_callback_exception_result():
+    result = Exception()
+    url = "http://example.com/"
+
+    def request_callback(request):
+        return result
+
+    @responses.activate
+    def run():
+        responses.add_callback(responses.GET, url, request_callback)
+
+        with pytest.raises(Exception) as e:
+            requests.get(url)
+
+        assert e.value is result
+
+    run()
+    assert_reset()
+
+
+def test_callback_exception_body():
+    body = Exception()
+    url = "http://example.com/"
+
+    def request_callback(request):
+        return (200, {}, body)
+
+    @responses.activate
+    def run():
+        responses.add_callback(responses.GET, url, request_callback)
+
+        with pytest.raises(Exception) as e:
+            requests.get(url)
+
+        assert e.value is body
+
+    run()
+    assert_reset()
+
+
 def test_callback_no_content_type():
     body = b"test callback"
     status = 400

pip install -e .[tests]
parse_log_pytest
requirements.txt
apt-get update apt-get install -y gcc
3.9
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
certifi==2025.1.31
charset-normalizer==3.4.1
coverage==4.5.4
exceptiongroup==1.2.2
flake8==7.2.0
idna==3.10
iniconfig==2.1.0
MarkupSafe==3.0.2
mccabe==0.7.0
packaging==24.2
pluggy==1.5.0
pycodestyle==2.13.0
pyflakes==3.3.2
pytest==8.3.5
pytest-cov==2.10.1
pytest-localserver==0.9.0.post0
requests==2.32.3
-e git+https://github.com/getsentry/responses.git@f89de1bfa616655d6b5fa0e0d5836113357ed986#egg=responses
six==1.17.0
tomli==2.2.1
urllib3==2.3.0
Werkzeug==3.1.3

name: responses
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:
      - certifi==2025.1.31
      - charset-normalizer==3.4.1
      - coverage==4.5.4
      - exceptiongroup==1.2.2
      - flake8==7.2.0
      - idna==3.10
      - iniconfig==2.1.0
      - markupsafe==3.0.2
      - mccabe==0.7.0
      - packaging==24.2
      - pluggy==1.5.0
      - pycodestyle==2.13.0
      - pyflakes==3.3.2
      - pytest==8.3.5
      - pytest-cov==2.10.1
      - pytest-localserver==0.9.0.post0
      - requests==2.32.3
      - six==1.17.0
      - tomli==2.2.1
      - urllib3==2.3.0
      - werkzeug==3.1.3
prefix: /opt/conda/envs/responses

test_responses.py::test_callback_exception_body
test_responses.py::test_arbitrary_status_code
test_responses.py::test_response test_responses.py::test_response_with_instance test_responses.py::test_replace[http://example.com/two-http://example.com/two] test_responses.py::test_replace[original1-replacement1] test_responses.py::test_replace[http://example\\.com/two-http://example\\.com/two] test_responses.py::test_replace_error[http://example.com/one-http://example\\.com/one] test_responses.py::test_replace_error[http://example\\.com/one-http://example.com/one] test_responses.py::test_remove test_responses.py::test_response_equality[args10-kwargs10-args20-kwargs20-True] test_responses.py::test_response_equality[args11-kwargs11-args21-kwargs21-False] test_responses.py::test_response_equality[args12-kwargs12-args22-kwargs22-False] test_responses.py::test_response_equality[args13-kwargs13-args23-kwargs23-True] test_responses.py::test_response_equality_different_objects test_responses.py::test_connection_error test_responses.py::test_match_querystring test_responses.py::test_match_empty_querystring test_responses.py::test_match_querystring_error test_responses.py::test_match_querystring_regex test_responses.py::test_match_querystring_error_regex test_responses.py::test_match_querystring_auto_activates test_responses.py::test_accept_string_body test_responses.py::test_accept_json_body test_responses.py::test_no_content_type test_responses.py::test_throw_connection_error_explicit test_responses.py::test_callback test_responses.py::test_callback_exception_result test_responses.py::test_callback_no_content_type test_responses.py::test_regular_expression_url test_responses.py::test_custom_adapter test_responses.py::test_responses_as_context_manager test_responses.py::test_activate_doesnt_change_signature test_responses.py::test_activate_mock_interaction test_responses.py::test_activate_doesnt_change_signature_with_return_type test_responses.py::test_activate_doesnt_change_signature_for_method test_responses.py::test_response_cookies test_responses.py::test_response_callback test_responses.py::test_response_filebody test_responses.py::test_assert_all_requests_are_fired test_responses.py::test_allow_redirects_samehost test_responses.py::test_handles_unicode_querystring test_responses.py::test_handles_unicode_url test_responses.py::test_headers test_responses.py::test_legacy_adding_headers test_responses.py::test_multiple_responses test_responses.py::test_multiple_urls test_responses.py::test_passthru test_responses.py::test_method_named_param test_responses.py::test_passthru_unicode test_responses.py::test_custom_target
responses.py
responses.py