diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst
index 2660524b..a571bfd1 100644
--- a/docs/versionhistory.rst
+++ b/docs/versionhistory.rst
@@ -3,6 +3,11 @@ Version history
This library adheres to `Semantic Versioning 2.0 `_.
+**UNRELEASED**
+
+- Fixed ``RuntimeError`` on asyncio when a ``CancelledError`` is raised from a task spawned through
+ a ``BlockingPortal`` (`#357 `_)
+
**3.3.0**
- Added asynchronous ``Path`` class
diff --git a/src/anyio/from_thread.py b/src/anyio/from_thread.py
index 26d4a262..d845f993 100644
--- a/src/anyio/from_thread.py
+++ b/src/anyio/from_thread.py
@@ -162,7 +162,7 @@ async def stop(self, cancel_remaining: bool = False) -> None:
async def _call_func(self, func: Callable, args: tuple, kwargs: Dict[str, Any],
future: Future) -> None:
def callback(f: Future) -> None:
- if f.cancelled():
+ if f.cancelled() and self._event_loop_thread_id not in (None, threading.get_ident()):
self.call(scope.cancel)
try:
diff --git a/tests/test_from_thread.py b/tests/test_from_thread.py
index 5339ea88..63fbc36c 100644
--- a/tests/test_from_thread.py
+++ b/tests/test_from_thread.py
@@ -376,3 +376,14 @@ def taskfunc(*, task_status: TaskStatus) -> None:
future, start_value = portal.start_task(
taskfunc, name='testname') # type: ignore[arg-type]
assert start_value == 'testname'
+
+ @pytest.mark.parametrize('anyio_backend', ['asyncio'])
+ async def test_asyncio_run_sync_called(self, caplog):
+ """Regression test for #357."""
+ async def in_loop():
+ raise CancelledError
+
+ async with BlockingPortal() as portal:
+ await to_thread.run_sync(portal.start_task_soon, in_loop)
+
+ assert not caplog.text