Repository: qtile/qtile

Issue:
App crashes when sending a dbus notification
### The issue:

See https://github.com/ciderapp/Cider/issues/1475 for more details. 

After playing a few songs with Cider App, the app will crash when changing songs with the following error message in the logs. I am not sure which side is responsible for the bug. 

```
[395803:0929/171008.500207:ERROR:libnotify_notification.cc(49)] notify_notification_close: domain=1236 code=36 message="GDBus.Error:com.dubstepdish.dbus.next.ServiceError: The service interface raised an error: list index out of range.
['  File "/usr/lib/python3.10/site-packages/dbus_next/message_bus.py", line 712, in _process_message\n    handler(msg, send_reply)\n', '  File "/usr/lib/python3.10/site-packages/dbus_next/message_bus.py", line 731, in handler\n    result = method.fn(interface, *args)\n', '  File "/usr/lib/python3.10/site-packages/libqtile/notify.py", line 88, in CloseNotification\n    self.manager.close(nid)\n', '  File "/usr/lib/python3.10/site-packages/libqtile/notify.py", line 203, in close\n    notif = self.notifications[nid]\n']"
Trace/breakpoint trap (core dumped)
```

### Required:

- [X] I have searched past issues to see if this bug has already been reported.


Discussion:
I suspect what's happening is that an invalid notification ID is being provided by the Cider app when trying to close the notification. The qtile notification server raises the `IndexError` as it doesn't recognise the ID.

However, I've no idea _why_ Cider is sending the wrong ID.
No idea. For now, I disabled dbus notifications in cider, which doesn't fix anything but does keep it from crashing. Perhaps in qtile you can add a soft check if the index exists and otherwise throw a warning or error but prevent a crash?
That's certainly an option. However, if the error is by a third party app (e.g. Cider) then it would be correct for qtile to raise an error rather than swallow it silently. A dbus error really shouldn't crash the app though.

The other scenario is that qtile's notification server implementation is incorrect, in which case, we should fix the bug!
yeah that makes sense. It really is the other app crashing when it gets the error. I will close this for now. If they determine they are sending the correct values, I will circle back. 
When the notification is created, we send an ID number, that ID should be used to close the notification.
Cider developers indicated it was an issue with libnotify:

> This is a libnotify error. Try re-enabling notifications and reinstalling the libnotify package, if that does not work check the [Arch Wiki page](https://wiki.archlinux.org/title/Desktop_notifications) and make sure you have a notification server installed and running.
>
> Unfortunately, we cannot provide much more support for this kind of issue. Best of luck.
I'd interpret that as them saying it's not a Cider issue. I'm not 100% convinced it's not a qtile issue!
Any steps I can assist with to confirm?
Yes. Can you run `dbus-monitor "interface='org.freedesktop.Notifications'"` in a terminal and then recreate the above issue. Once you've got the issue, post the output of `dbus-monitor` here.
Let me add something to this ticket - I think I'm in a similar situation and I didn't want to create a new issue (I will when you guys suggest me to do so).

I'm using [this little go program](https://github.com/maximbaz/yubikey-touch-detector) to get notifications when it's required to touch my Yubikey. The program works fine and notification shows - but the problem is, that it's not going away after touching the security key (which is an expected behavior). With dunst it works fine, but when using Notify widget, I have this message in detector's output:

```
ERRO[2022-11-06T21:34:56+01:00] Cannot close notification: The service interface raised an error: list index out of range.
['  File "/f/.local/share/virtualenvs/qtile/lib/python3.10/site-packages/dbus_next/message_bus.py", line 712, in _process_message\n    handler(msg, send_reply)\n', '  File "/f/.local/share/virtualenvs/qtile/lib/python3.10/site-packages/dbus_next/message_bus.py", line 731, in handler\n    result = method.fn(interface, *args)\n', '  File "/f/.local/share/virtualenvs/qtile/lib/python3.10/site-packages/libqtile/notify.py", line 88, in CloseNotification\n    self.manager.close(nid)\n', '  File "/f/.local/share/virtualenvs/qtile/lib/python3.10/site-packages/libqtile/notify.py", line 203, in close\n    notif = self.notifications[nid]\n']
```

At first I've created an [issue in detector's repository](https://github.com/maximbaz/yubikey-touch-detector/issues/33) to ask the author if it can be their software's fault. It seems not - also the fact that detector works fine with dunst is kinda proving this might be qtile's issue.

To duplicate the issue, I've just added Notify widget to my stausbar with default settings (important one is default_timeout, set to 600 seconds - thanks to that I can see my notification isn't going away and it won't before 600 secs or me closing it by clicking on it).

As @elParaguayo suggested, I've run dbus-monitor while duplicating the issue. Here is the output:

```
~ ❯ dbus-monitor "interface='org.freedesktop.Notifications'"                                                                                                                                                                              8s
signal time=1667766889.411224 sender=org.freedesktop.DBus -> destination=:1.43 serial=2 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
   string ":1.43"
signal time=1667766889.411262 sender=org.freedesktop.DBus -> destination=:1.43 serial=4 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameLost
   string ":1.43"
method call time=1667766894.155336 sender=:1.42 -> destination=org.freedesktop.Notifications serial=3 path=/org/freedesktop/Notifications; interface=org.freedesktop.Notifications; member=Notify
   string "yubikey-touch-detector"
   uint32 0
   string "yubikey-touch-detector"
   string "YubiKey is waiting for a touch"
   string ""
   array [
   ]
   array [
   ]
   int32 0
method call time=1667766896.991990 sender=:1.42 -> destination=org.freedesktop.Notifications serial=4 path=/org/freedesktop/Notifications; interface=org.freedesktop.Notifications; member=CloseNotification
   uint32 6
```

I also have [a screenshot](https://i.imgur.com/8pdr780.png) - the notification is still there, in the top right corner.

I'm using the current master verion on qtile:

```
~ ❯ qtile --version                                                                                                                                                                                                                   7m 43s
0.22.2.dev36+ga7663d0e
```

If more data is needed, please let me know and I'll provide it asap.

Edit: I've just notified: when the problem occurrs, I have this in qtile.log:

```
2022-11-06 21:48:19,097 ERROR libqtile loop.py:_handle_exception():L63 initializer for ctype 'PangoLayout *' must be a cdata pointer, not NoneType
2022-11-06 21:48:19,098 ERROR libqtile loop.py:_handle_exception():L63 initializer for ctype 'PangoLayout *' must be a cdata pointer, not NoneType
2022-11-06 21:48:19,098 ERROR libqtile loop.py:_handle_exception():L63 initializer for ctype 'PangoLayout *' must be a cdata pointer, not NoneType
2022-11-06 21:48:19,098 ERROR libqtile loop.py:_handle_exception():L63 initializer for ctype 'PangoLayout *' must be a cdata pointer, not NoneType
```
Thanks for this.

This does suggest it's a qtile issue which is what I suspected. I'll take a look when I can.
Oh, one more thing: this ERROR in loop.py I've added to my reply: it seems it's in qtile.log every time I invoke notify-send, not just with yubikey detector. Sorry for false information.

Resolving patch:
diff --git a/libqtile/notify.py b/libqtile/notify.py
--- a/libqtile/notify.py
+++ b/libqtile/notify.py
@@ -200,11 +200,9 @@ def show(self, *args, **kwargs):
             return (notif, self.add(notif))
 
         def close(self, nid):
-            notif = self.notifications[nid]
-
             for callback in self.close_callbacks:
                 try:
-                    callback(notif)
+                    callback(nid)
                 except Exception:
                     logger.exception("Exception in notifier close callback")
 


Test patch: