diff --git a/homeassistant/components/html5/notify.py b/homeassistant/components/html5/notify.py
index 0a6cc689292..a6ca769250f 100644
--- a/homeassistant/components/html5/notify.py
+++ b/homeassistant/components/html5/notify.py
@@ -533,7 +533,9 @@ class HTML5NotificationService(BaseNotificationService):
if response.status_code == 410:
_LOGGER.info("Notification channel has expired")
reg = self.registrations.pop(target)
- if not save_json(self.registrations_json_path, self.registrations):
+ try:
+ save_json(self.registrations_json_path, self.registrations)
+ except HomeAssistantError:
self.registrations[target] = reg
_LOGGER.error("Error saving registration")
else:
diff --git a/tests/components/html5/test_notify.py b/tests/components/html5/test_notify.py
index 116b4437d61..1614555c493 100644
--- a/tests/components/html5/test_notify.py
+++ b/tests/components/html5/test_notify.py
@@ -527,3 +527,46 @@ async def test_send_fcm_without_targets(hass, hass_client):
assert mock_wp.mock_calls[0][1][0] == SUBSCRIPTION_5["subscription"]
# Third mock_call checks the status_code of the response.
assert mock_wp.mock_calls[2][0] == "().send().status_code.__eq__"
+
+
+async def test_send_fcm_expired(hass, hass_client):
+ """Test that the FCM target is removed when expired."""
+ registrations = {"device": SUBSCRIPTION_5}
+ await mock_client(hass, hass_client, registrations)
+
+ with (
+ patch("homeassistant.components.html5.notify.WebPusher") as mock_wp,
+ patch("homeassistant.components.html5.notify.save_json"),
+ ):
+ mock_wp().send().status_code = 410
+ await hass.services.async_call(
+ "notify",
+ "notify",
+ {"message": "Hello", "target": ["device"], "data": {"icon": "beer.png"}},
+ blocking=True,
+ )
+ # "device" should be removed when expired.
+ assert "device" not in registrations
+
+
+async def test_send_fcm_expired_save_fails(hass, hass_client):
+ """Test that the FCM target remains after expiry if save_json fails."""
+ registrations = {"device": SUBSCRIPTION_5}
+ await mock_client(hass, hass_client, registrations)
+
+ with (
+ patch("homeassistant.components.html5.notify.WebPusher") as mock_wp,
+ patch(
+ "homeassistant.components.html5.notify.save_json",
+ side_effect=HomeAssistantError(),
+ ),
+ ):
+ mock_wp().send().status_code = 410
+ await hass.services.async_call(
+ "notify",
+ "notify",
+ {"message": "Hello", "target": ["device"], "data": {"icon": "beer.png"}},
+ blocking=True,
+ )
+ # "device" should still exist if save fails.
+ assert "device" in registrations