Skip to content

Commit 2eb3508

Browse files
Patch76claude
andcommitted
fix(tools_integrations): Path 4 idempotency keys on structured code only
Drop the generic ``"not found" in error_msg.lower()`` substring fallback from the Path 4 (config subentry) idempotent-success branch. The check now relies solely on the structured ``error_code == "not_found"`` from the HA ``config_entries/subentries/delete`` response. Generic substring matching was too permissive: HA error messages like "Repository not found" or "Integration not found" returned by unrelated failure modes would have silently classified as idempotent success, masking real errors. The structured ``code: "not_found"`` is the contract HA uses for this operation; if a future HA version emits a different code, the tool surfaces SERVICE_CALL_FAILED rather than mis-classifying — the safer default for a destructive operation. Adopts Gemini Code Assist finding on PR #1424 (inline thread on the substring check). Existing Path 4 unit test (test_delete_helpers_integrations_subentry_not_found_is_idempotent) mocks the response with ``code: "not_found"`` so it remains the canonical contract pin; no test changes needed. Sibling-sweep across the other three idempotent branches confirmed they already key on structured discriminators (Path 1: state+registry probes, Path 2: lookup-helper reason field, Path 3: HomeAssistantAPIError.status_code). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c13e064 commit 2eb3508

1 file changed

Lines changed: 11 additions & 6 deletions

File tree

src/ha_mcp/tools/tools_integrations.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1639,13 +1639,18 @@ async def _delete_config_subentry(
16391639
if not isinstance(result, dict) or not result.get("success"):
16401640
error = result.get("error", "Operation failed")
16411641
error_msg = websocket_error_message(error)
1642-
# Detect "subentry already absent" by HA's not_found code or a
1643-
# readable message containing "not found" — return idempotent
1644-
# success in line with the tool's idempotentHint. The HA
1645-
# config_entries/subentries/delete handler raises with
1646-
# code="not_found" when entry_id or subentry_id is missing.
1642+
# Detect "subentry already absent" by HA's structured
1643+
# ``code="not_found"`` only. A generic ``"not found" in
1644+
# error_msg`` substring match was rejected because it can
1645+
# collide with unrelated HA error messages (e.g.
1646+
# "repository not found", "integration not found") and
1647+
# silently classify a real failure as idempotent success.
1648+
# The HA ``config_entries/subentries/delete`` handler raises
1649+
# with ``code="not_found"`` when entry_id or subentry_id is
1650+
# missing; if a future HA version uses a different code, we
1651+
# raise SERVICE_CALL_FAILED instead — safer than mis-classifying.
16471652
error_code = error.get("code") if isinstance(error, dict) else None
1648-
if error_code == "not_found" or "not found" in error_msg.lower():
1653+
if error_code == "not_found":
16491654
return {
16501655
"success": True,
16511656
"action": "delete",

0 commit comments

Comments
 (0)