Skip to content

Task was destroyed but it is pending on HA shutdown (control_queue, window_queue) #1967

@paulomac1000

Description

@paulomac1000

Description

On every Home Assistant restart/shutdown, the following errors appear in logs for each Better Thermostat entity:

runner.py: Error doing job: Task was destroyed but it is pending!
  (task: <Task pending name='Task-XXXXX' coro=<control_queue()
   running at /config/custom_components/better_thermostat/utils/controlling.py:61>
   wait_for=<Future pending cb=[Task.task_wakeup()]>>)

runner.py: Error doing job: Task was destroyed but it is pending!
  (task: <Task pending name='Task-XXXXX' coro=<window_queue()
   running at /config/custom_components/better_thermostat/events/window.py:78>
   wait_for=<Future pending cb=[Task.task_wakeup()]>>)

Root cause: In climate.py lines 330–332, queue consumer tasks are created with bare asyncio.create_task() which are not tracked by Home Assistant's lifecycle management. When HA shuts down, these tasks are still pending (blocked on await queue.get()) and Python emits warnings.

Impact: Cosmetic only — does not affect runtime behavior, but pollutes logs and makes it harder to identify real errors.

Steps to Reproduce

  1. Install Better Thermostat integration with at least one climate entity configured
  2. Use Home Assistant normally (entities work correctly)
  3. Restart or shutdown Home Assistant (Developer Tools → Restart, or stop HA service)
  4. Check Home Assistant logs

Expected behavior:

Clean shutdown without "Task was destroyed but it is pending!" errors.

Actual behavior:

Two error messages appear per each Better Thermostat entity (one for control_queue, one for window_queue if window sensor is configured).

Versions and HW

Home Assistant: 2025.6.x
Better Thermostat: 1.7.0
TRV(s): N/A (issue is independent of TRV model)

Debug data

diagnostic data

{
  "N/A - issue occurs at shutdown, not related to specific entity configuration"
}

debug log

The relevant log entries are included in the Description section above. This issue occurs at HA shutdown, so standard debug logging doesn't capture additional useful information.

graphs

N/A — this is a shutdown/cleanup issue, not a calibration or control issue.

Additional Information

Proposed fix:

Option A (preferred) — Use hass.async_create_task() inside async_added_to_hass():

async def async_added_to_hass(self):
    ...
    self._control_queue_task_handle = self.hass.async_create_task(
        control_queue(self), name=f"bt_control_{self.name}"
    )
    if self.window_id is not None:
        self._window_queue_task_handle = self.hass.async_create_task(
            window_queue(self), name=f"bt_window_{self.name}"
        )

Option B — Store task references and cancel in async_will_remove_from_hass():

def __init__(self, ...):
    ...
    self._queue_tasks = []

# After creating tasks:
self._queue_tasks.append(asyncio.create_task(control_queue(self)))

async def async_will_remove_from_hass(self):
    for task in self._queue_tasks:
        task.cancel()
    await asyncio.gather(*self._queue_tasks, return_exceptions=True)

Note: window_queue() in events/window.py already handles asyncio.CancelledError (lines 102–106), so cancellation should work cleanly.

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions