Skip to content

Commit 8c0046f

Browse files
fix(cli): ignore CancelledError in background task done callback (#839)
CLI teardown cancels pending sync_entity_vectors tasks; _log_task_failure treated that as an unhandled callback exception because CancelledError is a BaseException. Skip cancelled tasks and add regression tests. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 60ec672 commit 8c0046f

2 files changed

Lines changed: 42 additions & 0 deletions

File tree

src/basic_memory/deps/services.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,8 +447,12 @@ def schedule(self, task_name: str, **payload: Any) -> None:
447447

448448

449449
def _log_task_failure(completed: asyncio.Task) -> None:
450+
if completed.cancelled():
451+
return
450452
try:
451453
completed.result()
454+
except asyncio.CancelledError:
455+
return
452456
except Exception as exc: # pragma: no cover
453457
logger.exception("Background task failed", error=str(exc))
454458

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""Tests for background task done-callback error handling."""
2+
3+
import asyncio
4+
from unittest.mock import patch
5+
6+
import pytest
7+
8+
from basic_memory.deps.services import _log_task_failure
9+
10+
11+
@pytest.mark.asyncio
12+
async def test_log_task_failure_ignores_cancelled_task():
13+
async def slow():
14+
await asyncio.sleep(10)
15+
16+
task = asyncio.create_task(slow())
17+
task.cancel()
18+
with pytest.raises(asyncio.CancelledError):
19+
await task
20+
21+
with patch("basic_memory.deps.services.logger.exception") as mock_exc:
22+
_log_task_failure(task)
23+
mock_exc.assert_not_called()
24+
25+
26+
@pytest.mark.asyncio
27+
async def test_log_task_failure_logs_real_exception():
28+
async def boom():
29+
raise ValueError("sync failed")
30+
31+
task = asyncio.create_task(boom())
32+
with pytest.raises(ValueError):
33+
await task
34+
35+
with patch("basic_memory.deps.services.logger.exception") as mock_exc:
36+
_log_task_failure(task)
37+
mock_exc.assert_called_once()
38+
assert "sync failed" in str(mock_exc.call_args)

0 commit comments

Comments
 (0)