You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hyundai / Kia Connect version: 4.26.1 (confirmed still present, unchanged, in 4.26.5 — the latest release
as of 2026-08-17)
Python version: 3.14.6
Operating System: Alpine Linux 3.24.1 (inside the official homeassistant/home-assistant Docker image),
Home Assistant Core 2026.8.1, via the kia_uvo custom integration (v3.10.0)
Description
_force_refresh_vehicle_state_ccs2 in KiaUvoApiEU.py contains an unconditional, blocking sleep(25):
In Home Assistant (via kia_uvo), this runs inside hass.async_add_executor_job, i.e. on a plain ThreadPoolExecutor thread — not an asyncio task. That thread is not cancellable through Home
Assistant's normal shutdown/reload machinery: HA can only wait for it to finish or time out its shutdown
grace period and force-kill.
This call can also fire automatically, with no user action, on the very first coordinator update after
any Home Assistant restart — VehicleManager.check_and_force_update_vehicle triggers a force-refresh
whenever (now - vehicle.last_updated_at).total_seconds() > force_refresh_interval, and last_updated_at reflects the vehicle's own last-known server timestamp, which can already be well past
that threshold at login (e.g. right after a fresh Home Assistant restart querying a car that hasn't sent
a real update in a while). With kia_uvo's own default force_refresh_interval (24h), this is easy to
trip on the very next restart after any real gap in activity.
I hit this in production: a Home Assistant instance crash-looped roughly every ~30 seconds (Docker log
signature: Found N non-daemonic threads, i.e. a forced-shutdown-timeout, not a clean exit). Each fresh
process immediately re-entered this same blocking path before the previous instance's 25s sleep thread
had necessarily finished, self-sustaining the loop. I could not capture the original in-process traceback
(rotated out of logs by the time I diagnosed this), but the mechanism above is independently reproducible
from the source alone and fully explains the observed cadence.
Two related, already-fixed issues (#1786, #1806, fixed via #1250 / released in 4.25.4) addressed the KeyError: 'resMsg' half of this same code path — the wake-then-read pattern itself (including this sleep(25)) is intentional and, per #1250's own PR description, was chosen specifically to avoid a
poll-loop. That tradeoff seems reasonable for a plain script, but is risky inside any framework (Home
Assistant included) that runs this on a thread it may need to cancel or wait out during a restart/reload.
What I Did
Reproduced the trigger condition by reasoning through the actual installed source
(VehicleManager.check_and_force_update_vehicle, coordinator.py's _async_update_data) rather than a
fresh crash, since the original traceback had rotated out of my logs by the time I diagnosed this:
# VehicleManager.py
if (started_at_utc - vehicle.last_updated_at).total_seconds() > force_refresh_interval:
self.force_refresh_vehicle_state(vehicle_id) # -> ... -> sleep(25) on an HA executor thread
Also confirmed kia_uvo's no_force_refresh_hour_start/_finish options cannot be configured to block
this trigger for all 24 hours (the boundary-check logic in kia_uvo's coordinator.py is structurally
incapable of an always-false result across both branches) — the only working mitigation is setting force_refresh_interval very high, which just delays the exposure rather than removing it.
Suggested fixes, roughly in order of how much they change:
Add a real bounded timeout to the whole force-refresh operation (wake + sleep + read), independent of
the per-request HTTP timeout already fixed in All HTTP requests missing timeout parameter (139 calls across 10 files) #1151 — so a caller can enforce "give up after N seconds"
around the blocking section as a whole.
Replace the unconditional sleep(25) with a short poll loop against /latest (checking whether the
snapshot has actually advanced) with a bounded max wait — the alternative fix(EU): simpler wake+sleep+read CCS2 force refresh (alternative to #1184) #1250 itself considered and
set aside, but worth revisiting given this failure mode.
At minimum, document that _force_refresh_vehicle_state_ccs2 blocks for ~25s and should never be
called from a thread a host application might need to cancel/await during shutdown — so integrations
like kia_uvo can guard against calling it near a restart/reload.
Workaround in place on my end: set force_refresh_interval to a very large value so the automatic
trigger doesn't fire during any realistic monitoring window. This avoids the automatic path but not a
manually-triggered "Force Refresh" that happens to land near a restart.
as of 2026-08-17)
homeassistant/home-assistantDocker image),Home Assistant Core 2026.8.1, via the
kia_uvocustom integration (v3.10.0)Description
_force_refresh_vehicle_state_ccs2inKiaUvoApiEU.pycontains an unconditional, blockingsleep(25):In Home Assistant (via
kia_uvo), this runs insidehass.async_add_executor_job, i.e. on a plainThreadPoolExecutorthread — not an asyncio task. That thread is not cancellable through HomeAssistant's normal shutdown/reload machinery: HA can only wait for it to finish or time out its shutdown
grace period and force-kill.
This call can also fire automatically, with no user action, on the very first coordinator update after
any Home Assistant restart —
VehicleManager.check_and_force_update_vehicletriggers a force-refreshwhenever
(now - vehicle.last_updated_at).total_seconds() > force_refresh_interval, andlast_updated_atreflects the vehicle's own last-known server timestamp, which can already be well pastthat threshold at login (e.g. right after a fresh Home Assistant restart querying a car that hasn't sent
a real update in a while). With
kia_uvo's own defaultforce_refresh_interval(24h), this is easy totrip on the very next restart after any real gap in activity.
I hit this in production: a Home Assistant instance crash-looped roughly every ~30 seconds (Docker log
signature:
Found N non-daemonic threads, i.e. a forced-shutdown-timeout, not a clean exit). Each freshprocess immediately re-entered this same blocking path before the previous instance's 25s sleep thread
had necessarily finished, self-sustaining the loop. I could not capture the original in-process traceback
(rotated out of logs by the time I diagnosed this), but the mechanism above is independently reproducible
from the source alone and fully explains the observed cadence.
Two related, already-fixed issues (#1786, #1806, fixed via #1250 / released in 4.25.4) addressed the
KeyError: 'resMsg'half of this same code path — the wake-then-read pattern itself (including thissleep(25)) is intentional and, per #1250's own PR description, was chosen specifically to avoid apoll-loop. That tradeoff seems reasonable for a plain script, but is risky inside any framework (Home
Assistant included) that runs this on a thread it may need to cancel or wait out during a restart/reload.
What I Did
Reproduced the trigger condition by reasoning through the actual installed source
(
VehicleManager.check_and_force_update_vehicle,coordinator.py's_async_update_data) rather than afresh crash, since the original traceback had rotated out of my logs by the time I diagnosed this:
Also confirmed
kia_uvo'sno_force_refresh_hour_start/_finishoptions cannot be configured to blockthis trigger for all 24 hours (the boundary-check logic in
kia_uvo'scoordinator.pyis structurallyincapable of an always-false result across both branches) — the only working mitigation is setting
force_refresh_intervalvery high, which just delays the exposure rather than removing it.Suggested fixes, roughly in order of how much they change:
the per-request HTTP timeout already fixed in All HTTP requests missing timeout parameter (139 calls across 10 files) #1151 — so a caller can enforce "give up after N seconds"
around the blocking section as a whole.
sleep(25)with a short poll loop against/latest(checking whether thesnapshot has actually advanced) with a bounded max wait — the alternative fix(EU): simpler wake+sleep+read CCS2 force refresh (alternative to #1184) #1250 itself considered and
set aside, but worth revisiting given this failure mode.
_force_refresh_vehicle_state_ccs2blocks for ~25s and should never becalled from a thread a host application might need to cancel/await during shutdown — so integrations
like
kia_uvocan guard against calling it near a restart/reload.Workaround in place on my end: set
force_refresh_intervalto a very large value so the automatictrigger doesn't fire during any realistic monitoring window. This avoids the automatic path but not a
manually-triggered "Force Refresh" that happens to land near a restart.