Bug: KeyError: 'id' in RingEventListener._get_ring_event on a malformed push notification
Environment
ring_doorbell version: 0.9.14 (current PyPI release)
- Python version: 3.14
- Installed via: Home Assistant
ring integration (custom ring_doorbell dependency pin)
- Home Assistant Core version: core-2026.8.2
- Ring device model(s): floodlight_v2
Description
The FCM push listener crashes with an unhandled KeyError: 'id' when it
receives a notification whose data.event.ding object is present but does
not contain an id field. The exception propagates out of the
notification callback into firebase_messaging, which catches it at its
own layer and logs "Unexpected exception calling notification callback" —
so the process doesn't crash, but the event that triggered it is silently
dropped instead of being delivered as a RingEvent.
Traceback
Traceback (most recent call last):
File "/usr/local/lib/python3.14/site-packages/firebase_messaging/fcmpushclient.py", line 456, in _handle_data_message
self.callback(ret_val, msg.persistent_id, self.callback_context)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.14/site-packages/ring_doorbell/listen/eventlistener.py", line 297, in _on_notification
ring_event = self._get_ring_event(msg_data)
File "/usr/local/lib/python3.14/site-packages/ring_doorbell/listen/eventlistener.py", line 323, in _get_ring_event
event_id = int(event["ding"]["id"])
~~~~~~~~~~~~~^^^^^^
KeyError: 'id'
2026-08-28 09:21:39.447 ERROR (MainThread) [firebase_messaging.fcmpushclient] Unexpected exception calling notification callback
(The traceback repeated twice in a row in the log, roughly a second
apart, for what appeared to be a single physical event — possibly a retry
or a duplicate push from Ring's side.)
Root cause (from reading ring_doorbell/listen/eventlistener.py, 0.9.14)
def _get_ring_event(self, msg_data: dict) -> RingEvent | None:
if (android_config_str := msg_data.get("android_config")) is None or (
data_str := msg_data.get("data")
) is None:
_logger.debug(
"Unexpected alert type in fcm message data. Full message is:\n%s",
json.dumps(msg_data),
)
return None
android_config = json.loads(android_config_str)
data = json.loads(data_str)
event_category = android_config["category"]
event_kind = PUSH_NOTIFICATION_KINDS.get(event_category, "Unknown")
device = data["device"]
event = data["event"]
event_id = int(event["ding"]["id"]) # <-- crashes here
created_at = event["ding"]["created_at"]
create_seconds = parse_datetime(created_at).timestamp()
return RingEvent(...)
The function already handles one "unexpected shape" case gracefully (missing
android_config/data → debug-log and return None), but the rest of the
parsing — event["ding"]["id"], event["ding"]["created_at"],
event["ding"]["subtype"] — assumes a fixed shape with no fallback. In
this case event["ding"] existed as a dict, so it got past data["event"],
but that dict didn't include id. I don't have the raw payload captured
(it isn't logged before the crash), so I can't say what event category or
payload shape triggered it — happy to attach the raw msg_data if I can
capture one with debug logging enabled and it recurs.
Expected behavior
A notification shape the library doesn't fully recognize should be logged
and skipped (as already happens for the missing android_config/data
case above), not raise an unhandled exception out of the callback.
Suggested fix
Wrap the event["ding"][...] access the same way the top of the function
already handles the missing android_config/data case — e.g. .get()
with a debug/warning log and return None on missing keys — rather than
direct indexing that raises.
Workaround in use
Monkeypatching RingEventListener._get_ring_event in a small Home
Assistant custom component to catch KeyError and log+skip instead of
raising, until this is fixed upstream.
Bug:
KeyError: 'id'inRingEventListener._get_ring_eventon a malformed push notificationEnvironment
ring_doorbellversion: 0.9.14 (current PyPI release)ringintegration (customring_doorbelldependency pin)Description
The FCM push listener crashes with an unhandled
KeyError: 'id'when itreceives a notification whose
data.event.dingobject is present but doesnot contain an
idfield. The exception propagates out of thenotification callback into
firebase_messaging, which catches it at itsown layer and logs "Unexpected exception calling notification callback" —
so the process doesn't crash, but the event that triggered it is silently
dropped instead of being delivered as a
RingEvent.Traceback
(The traceback repeated twice in a row in the log, roughly a second
apart, for what appeared to be a single physical event — possibly a retry
or a duplicate push from Ring's side.)
Root cause (from reading
ring_doorbell/listen/eventlistener.py, 0.9.14)The function already handles one "unexpected shape" case gracefully (missing
android_config/data→ debug-log andreturn None), but the rest of theparsing —
event["ding"]["id"],event["ding"]["created_at"],event["ding"]["subtype"]— assumes a fixed shape with no fallback. Inthis case
event["ding"]existed as a dict, so it got pastdata["event"],but that dict didn't include
id. I don't have the raw payload captured(it isn't logged before the crash), so I can't say what event category or
payload shape triggered it — happy to attach the raw
msg_dataif I cancapture one with debug logging enabled and it recurs.
Expected behavior
A notification shape the library doesn't fully recognize should be logged
and skipped (as already happens for the missing
android_config/datacase above), not raise an unhandled exception out of the callback.
Suggested fix
Wrap the
event["ding"][...]access the same way the top of the functionalready handles the missing
android_config/datacase — e.g..get()with a debug/warning log and
return Noneon missing keys — rather thandirect indexing that raises.
Workaround in use
Monkeypatching
RingEventListener._get_ring_eventin a small HomeAssistant custom component to catch
KeyErrorand log+skip instead ofraising, until this is fixed upstream.