Error in undo-list-transfer-to-tree while saving or undoing in large Org file

What happened?

When I save my monolithic file (257k) or use undo-tree to undo a change, Emacs often freezes. With other (smaller) Org files, this doesn’t usually happen. I am mentioning both the “save” and the “undo” because the backtraces that I’ve managed to get are identical; the error happens in the undo-list-transfer-to-tree function. Spamming C-g during the hang does nothing, but after adding (toggle-debug-on-quit) to my config, I was able to generate these backtraces (spamming C-g, pressing it once didn’t immediately trigger the debugger):

Hang on save and undo generate an identical backtrace:

Debugger entered--Lisp error: (quit)
  #<subr undo-list-transfer-to-tree>()
  apply(#<subr undo-list-transfer-to-tree> nil)
  #<subr undo-tree-save-history>(nil overwrite)
  apply(#<subr undo-tree-save-history> (nil overwrite))
  (prog1 (apply fn args) (message ""))
  (let ((inhibit-message t) (save-silently t)) (prog1 (apply fn args) (message "")))
  (if init-file-debug (progn (apply fn args)) (let ((inhibit-message t) (save-silently t)) (prog1 (apply fn args) (message ""))))
  doom-shut-up-a(#<subr undo-tree-save-history> nil overwrite)
  apply(doom-shut-up-a #<subr undo-tree-save-history> (nil overwrite))
  undo-tree-save-history(nil overwrite)
  #<subr save-buffer>()
  funcall(#<subr save-buffer>)
  (let ((apheleia-mode (and apheleia-mode (memq arg '(nil 1))))) (funcall orig-fn))
  +format--inhibit-reformat-on-prefix-arg-a(#<subr save-buffer>)
  apply(+format--inhibit-reformat-on-prefix-arg-a #<subr save-buffer> nil)
  evil-write(nil nil nil nil nil)
  funcall-interactively(evil-write nil nil nil nil nil)
  evil-ex-call-command(nil "w" nil)
  funcall-interactively(evil-ex nil)

It’s possible the freeze on undo is related to garbage collection, because I also got this backtrace once:

;; Debugger entered--entering a function:
* undo-tree-post-gc()
  apply(gcmh-idle-garbage-collect nil)
  timer-event-handler([t 26059 39268 866324 nil gcmh-idle-garbage-collect nil nil 840999 nil])

Values of:

  • gcmh-mode: t
  • gcmh-low-cons-threshold: 800000
  • gcmh-high-cons-threshold: 33554432

I was able to get a CPU and memory profile during the undo-list-transfer-to-tree error (starting the profiler right before I tried to reproduce the error, and ending it right after the hang finished).


Loading data dump...


Loading data dump...

What did you expect to happen?

Org file should save almost instantly. Undo should work almost instantly, or at least not hang.

Steps to reproduce


  1. Start Emacs (new instance)
  2. Open
  3. Make a change
  4. Save :w


  1. Make change to org file
  2. Undo the change (u)

System information

Loading data dump...