Subject: 31.0.50; definition is void in file-notify

From: Al Haji-Ali <[email-masked]>
Date: Fri, 06 Jun 2025 11:16:44 +0100

I am running into an issue (on MacOS) where a file-watcher gets called with a specific set of actions, which lead to the error

,----
| file-notify--call-handler: Symbol's function definition is void: nil
`----

Here's the full stack trace:

,----
| Debugger entered--Lisp error: (void-function nil)
|   nil((63 attribute-changed "[redacted]"))
|   file-notify--call-handler(#s(file-notify--watch :directory "[redacted]" :filename "[redacted]" :callback nil) 63 attribute-changed "[redacted]" nil)
|   file-notify--handle-event(63 (renamed attribute-changed deleted) "[redacted]" nil)
|   file-notify--callback-kqueue((63 (rename attrib delete) "[redacted]"))
|   file-notify-handle-event((file-notify (63 (rename attrib delete) "[redacted]") file-notify--callback-kqueue))
|   funcall-interactively(file-notify-handle-event (file-notify (63 (rename attrib delete) "[redacted]") file-notify--callback-kqueue))
|   command-execute(file-notify-handle-event nil [(file-notify (63 (rename attrib delete) "[redacted]") file-notify--callback-kqueue)] t)
`----

and here's some code that replicates the error (I can't figure out now to replicate the event itself)

--8<---------------cut here---------------start------------->8---
(let* ((desc (car (hash-table-keys file-notify-descriptors)))
       (watch (gethash desc file-notify-descriptors))
       (action '(rename atttrib delete)))
  (when desc
    (file-notify-handle-event
     (make-file-notify
      :-event (list desc action (file-notify--watch-absolute-filename watch))
      :-callback 'file-notify--callback-kqueue))))
--8<---------------cut here---------------end--------------->8---

assuming that `file-notify-descriptors` has some entries (enabling `global-auto-revert-mode` would add such entries).

The issues seems to be that when processing the first action (`rename`), the function `file-notify--handle-event` treats it as 'delete' which calls `file-notify-rm-watch` which calls `file-notify--rm-descriptor` which has the line `(setf
(file-notify--watch-callback watch) nil)`

This modifies the hash to have a nil handler, which trips up
`file-notify--handle-event` when the actual `delete` event is processed.

The same effect can be seen if the actions are (rename delete) or (delete delete).