What is a backtrace & how to produce them

Eventually, you will encounter a cryptic error while using Emacs, but more information can be extracted from errors if you produce a backtrace from it. This should be your first step when dealing with any error you don’t immediately recognize — you will always be asked for one in bug reports, and they may help you locate and resolve the issue yourself.

:pushpin: See “How to debug issues” for a more general guide on dealing with problems in Emacs.

What is a backtrace?

A backtrace lays out the series of function calls that led to the error, and what arguments they were called with. This can tell you much more about where in Doom’s, Emacs’, or a package’s code base an error originated from.

How to make sense of a backtrace

For the sake of demonstration, here is the backtrace of an error I artificially created by typing M-x eval-expression RET (+ 1 nil):

Debugger entered--Lisp error: (wrong-type-argument number-or-marker-p nil)
  +(1 nil)
  eval-expression((+ 1 nil) nil nil 127)
  funcall-interactively(eval-expression (+ 1 nil) nil nil 127)
  command-execute(eval-expression record)
  execute-extended-command(nil "eval-expression" "eval-expression")
  funcall-interactively(execute-extended-command nil "eval-expression" "eval-expression")

Let me break this down:

  1. The first line will always be in the format:

    Debugger entered--Lisp error: (ERROR_TYPE [EXTRA_DATA...])

    ERROR_TYPE is the name of the error. Emacs lists its internal errors in its manual, and another Discourse post lists a few examples of common errors and how to interpret them. The exact format of EXTRA_DATA varies from error to error, however.

    In any case, here is what you can glean from this particular error, (wrong-type-argument number-or-marker-p nil):

    1. wrong-type-argument indicates a data type error. This means something in the expression (+ 1 nil) was expecting data of one type, but received data in another, illegal type, and cannot proceed.

    2. The first argument of a wrong-type-argument error is the name of a predicate function that the data failed to satisfy. In this case, number-or-marker-p. This suggests a number (or marker, but I won’t go into that here) was expected, and the actual data given was neither.

    3. The nil is the illegal data that was received instead of a number or marker.

  2. The next line of the backtrace is +(1 nil), and it will always be the block of code emitted the error.

    To spoil the mystery, + expects all its arguments to be numeric, so passing it a non-number will cause this type error.

    :pushpin: C-hf+ will reveal the + function’s documentation, which says:

    Return sum of any number of arguments, which are numbers or markers.

  3. The rest of the backtrace tells you the series of function calls that led to this point. For this contrived example they aren’t important, but more complex errors may require you work further backwards to find the true cause.

    In those cases, I suggest you read “Inspecting source code” for ways to directly inspect functions in the backtrace.

How to produce a backtrace

Simply, you set the debug-on-error variable to a non-nil value. Here are three ways to do so:

  • Start Emacs with emacs --debug-init. Use this for errors that occur at startup

  • Doom Emacs users should activate doom-debug-mode:

    • Evil users: SPChdd
    • Users that have disabled evil: C-hdd.
    • Otherwise: M-x doom-debug-mode
  • If the above don’t work, or you don’t use Doom, use M-x toggle-debug-on-error, instead.

Once debug-on-error is activated, go ahead and reproduce an error, and a new window will appear to display a backtrace.

:warning: Unfortunately, not all errors produce a backtrace. For example: when the error is emitted by the modeline or a post-command-hook function, Emacs suppresses them. This is done to protect against really destructive errors breaking Emacs’ UI.

When this happens let us know in your bug report, but try to include the full error message that appears in the *Messages* buffer. It may, at least, mention where the error was suppressed.

From bin/doom

If the error you encountered was emitted from bin/doom, a limited backtrace will already be displayed, but the full backtrace is written to ~/.emacs.d/.local/doom.error.log. Attach this file to your bug report.

From frozen Emacs

If Emacs freezes or hangs, you can produce a backtrace to determine what logic Emacs is hanging on with one of the following:

  • Send a USR2 signal to the Emacs process: pkill -USR2 emacs or kill -USR2 $EMACSPID.

    :pushpin: EMACSPID should be replaced with the process id of your frozen instance of Emacs. Use the commands pgrep emacs or ps aux | grep emacs to help you locate its PID.

  • Turn on debug-on-quit, mash C-g, and hope for the best.

:warning: Not all freezes can be recovered from, unfortunately. If the above doesn’t work, your only option is to forcibly kill the Emacs process.