fix(bluebubbles): track fire-and-forget mark_read task to prevent GC

The inbound webhook handler already stored its main handle_message
task in self._background_tasks (with a done-callback that discards
on completion), but the adjacent mark_read read-receipt task was
scheduled with a bare asyncio.create_task(...) and the return value
discarded.

Python's event loop only holds a weak reference to tasks returned by
create_task; the docs explicitly warn that untracked tasks can be
garbage-collected before completing. In the webhook-receipt path
this means a GC pass between task creation and the first await
inside mark_read silently drops the read receipt — users see their
messages as "unread" on the iMessage side even though the agent
processed them.

Route the task through the existing _background_tasks set with the
same done-callback pattern used for handle_message five lines up.
This commit is contained in:
Alexazhu 2026-04-18 14:48:05 +08:00
parent 73bccc94c7
commit 1b840329f2

View file

@ -910,9 +910,14 @@ class BlueBubblesAdapter(BasePlatformAdapter):
self._background_tasks.add(task)
task.add_done_callback(self._background_tasks.discard)
# Fire-and-forget read receipt
# Fire-and-forget read receipt — tracked in _background_tasks so
# the event loop's weak-reference tracking of asyncio.create_task()
# does not garbage-collect it before the receipt reaches the
# BlueBubbles server (Python docs warn about this pattern).
if self.send_read_receipts and session_chat_id:
asyncio.create_task(self.mark_read(session_chat_id))
mark_read_task = asyncio.create_task(self.mark_read(session_chat_id))
self._background_tasks.add(mark_read_task)
mark_read_task.add_done_callback(self._background_tasks.discard)
return web.Response(text="ok")