Skip to content

Event listener dies: KeyError 'id' in _get_ring_event — some Ring FCM payloads no longer contain ding.id #537

Description

@portsparky

Describe the bug

Since roughly 2026-07-19 (worsening through late July), some Ring FCM push notifications arrive with an event.ding object that has no id key. _get_ring_event() crashes on every such message, and after 3 sequential callback errors firebase_messaging shuts the push receiver down entirely — killing realtime events for all devices until the listener is restarted.

ERROR (MainThread) [firebase_messaging.fcmpushclient] Unexpected exception calling notification callback
Traceback (most recent call last):
  File ".../firebase_messaging/fcmpushclient.py", line 456, in _handle_data_message
    self.callback(ret_val, msg.persistent_id, self.callback_context)
  File ".../ring_doorbell/listen/eventlistener.py", line 297, in _on_notification
    ring_event = self._get_ring_event(msg_data)
  File ".../ring_doorbell/listen/eventlistener.py", line 323, in _get_ring_event
    event_id = int(event["ding"]["id"])
                   ~~~~~~~~~~~~~^^^^^^
KeyError: 'id'
ERROR (MainThread) [firebase_messaging.fcmpushclient] Shutting down push receiver due to 3 sequential errors of type ErrorType.NOTIFY

Environment

  • ring-doorbell 0.9.14 (via Home Assistant 2026.7.4, Python 3.14)
  • 13 Ring cameras (mix of battery + wired, indoor + outdoor)

Observations

  • Restarting the listener (reloading the HA integration) restores push, but it dies again within ~1–2 h once more id-less payloads arrive. 70+ occurrences of this exact traceback in the last few days of logs.
  • It appears device/notification-type dependent: two cameras stopped registering push events entirely around Jul 19 (their motions seemingly always produce the new payload), while other cameras continued working between crashes.
  • Current master still has the unguarded access at the same line, so this affects the latest code too.

Suggested fix

Treat a missing ding.id defensively in _get_ring_event (e.g. event["ding"].get("id"), falling back to a synthetic id or skipping with a debug log) so one unexpected payload variant cannot take down the whole receiver. I have debug logging enabled and will attach a sanitised capture of the offending payload when the next one lands.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions