Skip to content

RuntimeError: Session is closed during scheduled coordinator refresh causes periodic entity drops to Unavailable #322

Open

Description

@bthanos

Description

On scheduled polling cycles (every ~2 hours), the integration's DataUpdateCoordinator encounters an unhandled RuntimeError: Session is closed inside api.py:get_endpoint.

This causes the coordinator to raise UpdateFailed, temporarily marking all associated entities (sensor.generac_*, binary_sensor.generac_*_is_connected) as Unavailable for roughly 5–15 seconds until an inline retry/re-auth routine opens a new session and restores them.

Environment

  • Home Assistant Core: 2026.10+
  • Python Version: 3.14+
  • Integration: ha-generac (custom_components/generac)

Relevant Traceback

ERROR (MainThread) [custom_components.generac] Unexpected error refreshing Generac data
Traceback (most recent call last):
  File "/config/custom_components/generac/api.py", line 135, in get_endpoint
    async with self._session.get(url, headers=headers) as response:
  File "/usr/local/lib/python3.14/site-packages/aiohttp/client.py", line 1756, in __aenter__
    self._resp: _RetType_co = await self._coro
  File "/usr/local/lib/python3.14/site-packages/aiohttp/client.py", line 615, in _request
    raise RuntimeError("Session is closed")
RuntimeError: Session is closed

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/config/custom_components/generac/coordinator.py", line 49, in _async_update_data
    items = await self.api.async_get_data()
  File "/config/custom_components/generac/api.py", line 57, in async_get_data
    return await self.get_device_data()
  File "/config/custom_components/generac/api.py", line 60, in get_device_data
    apparatuses = await self.get_endpoint("/Apparatus/list")
  File "/config/custom_components/generac/api.py", line 159, in get_endpoint
    raise IOError(f"GET {url} failed: {type(ex).__name__}: {ex}") from ex
OSError: GET [https://app.mobilelinkgen.com/api/v5/Apparatus/list](https://app.mobilelinkgen.com/api/v5/Apparatus/list) failed: RuntimeError: Session is closed
ERROR (MainThread) [custom_components.generac] Error fetching generac data: GET [https://app.mobilelinkgen.com/api/v5/Apparatus/list](https://app.mobilelinkgen.com/api/v5/Apparatus/list) failed: RuntimeError: Session is Closed


Activity

  1. bthanos commented on Nov 9, 2026

    @bthanos
    Author

    Proposed Fix
    In custom_components/generac/api.py, guard against closed sessions before calling self._session.get(), and catch RuntimeError("Session is closed") to transparently re-instantiate the session and retry the request:

    import aiohttp

    async def get_endpoint(self, endpoint: str):
    # Ensure the session exists and is active before initiating a request
    if self._session is None or self._session.closed:
    if hasattr(self, "_hass") and self._hass:
    from homeassistant.helpers.aiohttp_client import async_get_clientsession
    self._session = async_get_clientsession(self._hass)
    else:
    self._session = aiohttp.ClientSession()

    url = f"{self._base_url}{endpoint}"
    headers = self._get_headers()
    
    try:
        async with self._session.get(url, headers=headers) as response:
            if response.status == 401:
                await self.login()
                headers = self._get_headers()
                async with self._session.get(url, headers=headers) as retry_response:
                    return await retry_response.json()
            elif response.status >= 400:
                text = await response.text()
                raise IOError(f"API returned status code {response.status} for {endpoint}: {text}")
            return await response.json()
    except RuntimeError as ex:
        if "Session is closed" in str(ex):
            if hasattr(self, "_hass") and self._hass:
                from homeassistant.helpers.aiohttp_client import async_get_clientsession
                self._session = async_get_clientsession(self._hass)
            else:
                self._session = aiohttp.ClientSession()
            headers = self._get_headers()
            async with self._session.get(url, headers=headers) as retry_resp:
                return await retry_resp.json()
        raise
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions