fix: prevent KeyError when setting up lock after previous deletion#595
fix: prevent KeyError when setting up lock after previous deletion#595raman325 wants to merge 11 commits intoFutureTense:mainfrom
Conversation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* upstream/main: chore: bump minimum HA version to 2026.4.0 fix: stop re-fetching masked usercodes every poll cycle feat: add Schlage WiFi lock provider fix: auto-dismiss retry lock notifications when lock succeeds feat: add Last Used event entity per code slot fix: slugify lock name when generating default notification script name fix(tests): restore accidentally merged test_get_code_empty_pin fix(akuvox): handle X916 user filtering where source_type is None test: add tests for `_update_lock_data` early exit conditions test: add coverage for reset_code_slot, update_slot_active_state, and _update_child_code_slots feat: add Akuvox lock provider with event routing fixes style: move imports to module level to fix ruff PLC0415 feat: Add auto-lock timer sensor with dashboard badge (FutureTense#509) test: add ping_node tests for ZWaveJS and base provider feat: ping dead node on first failure to attempt recovery style: apply ruff formatting fixes and add dead-node backoff tests fix: relax dead-node checks on connect/read operations fix: skip Z-Wave commands to dead nodes and add exponential backoff
When a lock entry is deleted, delete_coordinator() removes hass.data[DOMAIN] after a 20-second delay. If a new lock entry is then created, async_setup_services() crashes with KeyError accessing hass.data[DOMAIN]. Two fixes applied: - Use hass.data.setdefault() in async_setup_services to handle missing key - Guard delete_coordinator to skip removal if new config entries exist Fixes FutureTense#593 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #595 +/- ##
==========================================
+ Coverage 84.14% 90.06% +5.91%
==========================================
Files 10 28 +18
Lines 801 3471 +2670
==========================================
+ Hits 674 3126 +2452
- Misses 127 345 +218
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Fixes a race condition in the Keymaster Home Assistant integration where hass.data[DOMAIN] could be removed after deleting the last config entry, causing a KeyError during subsequent lock setup (issue #593).
Changes:
- Ensure
async_setup_services()recreateshass.data[DOMAIN]when missing to preventKeyError. - Guard delayed coordinator cleanup so it won’t remove domain data if new config entries already exist.
- Update/add tests for the
hass.data[DOMAIN]-missing scenario, and add new strategy-config-validation plan/design docs.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
custom_components/keymaster/services.py |
Recreates hass.data[DOMAIN] defensively before accessing it. |
custom_components/keymaster/__init__.py |
Prevents delayed cleanup from removing domain data when entries exist (race guard). |
tests/test_services.py |
Adds coverage to ensure async_setup_services works when the domain key was popped. |
tests/test_init.py |
Adjusts a test that previously relied on the KeyError behavior (now fixed). |
docs/plans/2026-03-10-strategy-config-validation.md |
Adds a large implementation plan document (appears unrelated to this bugfix PR). |
docs/plans/2026-03-10-strategy-config-validation-design.md |
Adds a design doc for the same strategy-validation work (also appears unrelated here). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address review feedback: use new_callable=AsyncMock instead of return_value=None to avoid TypeError from awaiting None. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pass the unloaded config entry ID to delete_coordinator so it can filter it out of async_entries(). This prevents the entry being unloaded from counting as an active entry, which would incorrectly block coordinator cleanup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Proposed change
When a lock entry is deleted,
delete_coordinator()removeshass.data[DOMAIN]after a 20-second delay. If the user creates a new lock entry before or after that cleanup fires,async_setup_services()crashes withKeyErrorbecause it assumeshass.data[DOMAIN]exists.Two fixes applied:
services.py— Addedhass.data.setdefault(DOMAIN, {})before accessinghass.data[DOMAIN], so the dict is recreated if it was previously removed.__init__.py— Added a guard indelete_coordinator()to skip domain data removal if new config entries already exist (handles the race condition where the 20-second delayed cleanup fires after a new entry is set up).Type of change
Additional information
🤖 Generated with Claude Code