fix: prevent GTK double-populate crash and KeyError in entity/callback cleanup#20
Merged
Merged
Conversation
…k cleanup
- Replace GTK model reads (_get_current_domains/_get_current_entities) with
Python-level caches (_last_loaded_domains/_last_loaded_entities) in
BaseCore._load_domains and BaseCore._load_entities to prevent the
gtk_list_tile_split assertion failure when on_ready fires twice in the same
GTK idle cycle (e.g. double page load on navigation)
- Reset _last_loaded_entities = None in on_change_domain when the domain
changes so the entity combo is always repopulated for the new domain
- Fix remove_tracked_entity: use self._entities.get(domain, {}).get(entity_id)
instead of self._entities[domain] to avoid KeyError on unknown domain; add
early return when entity_settings is None; use discard instead of remove to
avoid KeyError when action callback is not registered
- Fix remove_action_ready_callback: use discard instead of remove to avoid
KeyError when callback is not registered
- Add tests for new edge cases: unknown domain, unregistered callback, domain
cache reset, and double populate prevention
Agent-Logs-Url: https://github.com/gensyn/HomeAssistantPlugin/sessions/9be9980d-1f54-437e-aded-8f1fd67fdded
Co-authored-by: gensyn <36128035+gensyn@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Fix StreamController crashing with HomeAssistant Plugin
fix: prevent GTK double-populate crash and KeyError in entity/callback cleanup
May 7, 2026
Contributor
There was a problem hiding this comment.
Pull request overview
This pull request hardens the Home Assistant action UI against a GTK crash caused by rapid double-population of combo models, and makes backend cleanup methods resilient to race conditions that previously raised KeyError.
Changes:
- Replace GTK model state reads in
BaseCore._load_domains()/_load_entities()with Python-level “last loaded” caches to prevent doublepopulate()calls in the same GTK idle cycle. - Make backend deregistration paths idempotent by using
.get(...)lookups andset.discard()inremove_tracked_entity()andremove_action_ready_callback(). - Add/adjust unit tests covering unknown domain cleanup, missing callback removal, entity cache reset on domain change, and double-populate prevention.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
actions/cores/base_core/base_core.py |
Adds _last_loaded_domains/_last_loaded_entities caches, resets entity cache on domain change, and prevents redundant populate() calls. |
backend/home_assistant_backend.py |
Avoids KeyError during callback/entity cleanup by switching to discard() and safe .get() chaining with early returns. |
test/backend/test_backend_remove_tracked_entity.py |
Adds coverage for removing tracked entities when the domain is missing or the callback wasn’t registered. |
test/backend/test_backend_remove_action_ready_callback.py |
Adds coverage for removing a callback that was never registered. |
test/actions/cores/base_core/test_base_core_on_change_domain.py |
Verifies that changing the domain resets the entity cache so entities will repopulate. |
test/actions/cores/base_core/test_base_core_load_entities.py |
Updates tests to rely on the new cache behavior; adds a “no double populate on repeated call” case. |
test/actions/cores/base_core/test_base_core_load_domains.py |
Updates tests to rely on the new cache behavior; adds a “no double populate on repeated call” case. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Users experience a
gtk_list_tile_splitassertion SIGABRT when navigating back to a page with configured HA buttons. A previous fix only guarded the "not connected" path; the crash still fires whenon_ready()is called twice in the same GTK idle cycle while connected (double page-load observed in logs).GTK double-populate crash (
base_core.py)_load_domains/_load_entitiesusedget_item_amount()/get_item_at()to determine whether repopulation was needed. Because GTK processes model writes lazily, both rapid calls see stale widget state, each triggerspopulate(), and GTK's internal list-tile state is corrupted.Fix: replace GTK model reads with Python-level caches (
_last_loaded_domains,_last_loaded_entities). The cache is written synchronously on the first call, so a second rapid call skipspopulate()entirely._last_loaded_entitiesis also reset toNoneinsideon_change_domainwhen the domain actually changes, ensuring the entity combo is always repopulated for the new domain.KeyErrorcrashes in cleanup (home_assistant_backend.py)remove_tracked_entity: directself._entities[domain]access raisesKeyErrorwhen a domain disappears after a reconnect/reload.set.remove()raisesKeyErrorwhen the callback was never registered. Fixed with.get()chaining + early return onNone+discard().remove_action_ready_callback:set.remove()raisesKeyErrorunder the same race conditions. Fixed withdiscard().Tests
New cases cover: unknown domain in
remove_tracked_entity, unregistered callback in both remove methods, entity cache reset on domain change, and the double-populate prevention for both domain and entity combos.