Emacs Debugging Techniques


Published: 2022-07-17, Last Updated: 2025-02-26

If you are new to Emacs, you may run into some errors, especially after you copied some elisp snippets from the Internet or elsewhere. Don't panic! It happens, it's just part of the learning process. Even an experienced Emacs user could run into there issues from time to time.

Photo by Mike Newbry on Unsplash
Photo by Mike Newbry on Unsplash

But in order to ease the pain, I will introduce a bunch of Emacs built-in features to help you troubleshoot and eventually solve the problems by yourself in this post. Let's dive in!

Starting a Clean Emacs

When you have some problems/errors with built-in features, the first thing you may need to do is to run emacs -Q to start another clean Emacs instance without any configurations from your emacs.d.

Here is how to do it in details if you are not familiar with the terminal:

  1. If you use Linux/macOS, open a terminal, then run the command emacs -Q.

    ~emacs -Q~ on Linux
    emacs -Q on Linux

    If you prefer to use Emacs in a terminal or run Emacs on a server, you can append an argument -nw to the command, that is, emacs -Q -nw.

    P.S. If you want to know more about the command line arguments, you can look it up in the man manual (type man emacs in the terminal) or type emacs --help to see the help information, so as to get a better understanding of how to run Emacs from the command line.

  2. Or if you use Windows, you can run the cmd program first (type Super+r cmd ENTER), cd into the Emacs bin directory, then type runemacs.exe -Q to run it.

    ~emacs -Q~ on Windows cmd
    emacs -Q on Windows cmd

Troubleshooting a Problematic Package

emacs -Q helps to debug built-in features. But when it comes to debugging third-party packages, like the ones you install from melpa, we need to do a bit more, as we need to load the package.

There are roughly two ways to do that:

  1. Using the package machinery,
  2. Loading directly from a specific directory.

Whichever you take, the goal here is to only load that specific package so that there is zero possibility that it's introduced by other packages.

Using the package Machinery

For example, if we now have a problem with my ppcompile package, we can prepare a snippet (e.g. in /tmp/try-ppcompile.el) to prepare the debugging environment:

;; you don't need this if you never change this variable
(setq package-user-dir "~/.emacs.local.d/elpa")

;; init the package manager
(require 'package)
(package-initialize)

;; load the problematic package
(require 'ppcompile)

Now open up a terminal/cmd, type emacs -Q -l /tmp/try-ppcompile.el, and it will start a clean Emacs with ppcompile loaded. From there, we can investigate further to see the problem.

Loading Directly From a Specific Directory

There are times that you can't utilize the package machinery to load the package, for example, you git-clone a package and load it from your local file system directly. In that case, you can use Emacs' -L argument to specify the directory and load the package.

For example, type emacs -Q -L /tmp/ppcompile/ --eval "(require 'ppcompile)" to launch a new Emacs with the package and start debugging immediately.

Note that you can specify more than one directory with more -L.

Using --debug-init To Debug Start-up Errors

It's not uncommon that after you updated your emacs.d, Emacs just errored out when you ran it again.

Emacs errors out while starting up
Emacs errors out while starting up

Don't panic! Look carefully, and you will notice that Emacs has already told you how to troubleshoot it. (Pay attention to the second red line in the above screenshot)

Open a terminal, type emacs --debug-init, as Emacs tells us in the message, to start a new one. It will tell you where the error comes exactly from and the backtrace as well. Be patient and try to understand the problem, you will come up with a solution, hopefully! (The root cause in the screenshot is that the a-bug-function function is not defined)

Append ~--debug-init~ to run Emacs
Append --debug-init to run Emacs

Running M-x toggle-debug-on-error to Enable Debugging

You may see something like the below when you're using Emacs:

No Function Definition Error in the Echo Area
No Function Definition Error in the Echo Area

To simulate such an error, for example, say we have a silly welcome command as below:

(defun welcome ()
  (interactive)
  (this-func-not-exist))

Now when you type M-x welcome to invoke the command, you will see the error as in the above screenshot.

Now, type M-x toggle-debug-on-error to enable debugging (in case you don't know yet, here is how to type M-x toggle-debug-on-error on the keyboard: ALT+x toggle-debug-on-error RETURN), then it will bring up a new window with the backtrace when we hit M-x welcome once again.

elisp debugger backtrace 1: Symbol's function definition is void
elisp debugger backtrace 1: Symbol's function definition is void

From the backtrace, we can see that the error is due to the function of this-func-not-exist does not exist.

Setting the debug-on-message Variable

Other times, you may see some unexpected message on the echo area at the bottom. You can try to play around the debug-on-message variable to get the backtrace of where it comes from and figure it out.

For example, the below command will send out a message welcome who? when you invoke it:

(defun welcome ()
  (interactive)
  (message "welcome who?"))

Let's pretend that we now want to find out who print the nonsense message of welcome who, and we can utilize the debug-on-message variable to set up a regular expression, for instance, (setq debug-on-message "welcome who").

Once there is a message that matches the regexp, Emacs will show the backtrace, and we will find out who is the culprit there:

elisp debugger backtrace 2
elisp debugger backtrace 2

Other Debugging Helpers and Facilities

Here are variable ways to look up help docs in Emacs:

  1. C-h f to look up a function(C-h f means to type CTRL+h f in the keyboard)
  2. C-h v to look up a variable
  3. C-h k to look up a keybinding
  4. C-h m to look up which major mode and minor modes are enabled in the current buffer
  5. C-h l to view lossage, put it in another way, you will see the history of the invoked commands in a dedicated buffer

Last but not least, Emacs also has other machineries to help debug:

  1. Running M-x describe-char to see the properties of the character at point, for example, its font face.
  2. Leveraging the debug-on-entry function. Emacs starts the debugger automatically when your function has an error, also refer to debug-on-entry (Programming in Emacs Lisp) - www.gnu.org.
  3. Setting the debug-on-quit variable, please head over to debug-on-quit (Programming in Emacs Lisp) - www.gnu.org for details.
  4. Specifying --init-directory. Emacs 29 adds a command line argument of --init-directory to specify a directory other than ~/.emacs.d/ to run Emacs, where we can put our debugging code to do the investigation separately.

See also

comments powered by Disqus