22

Say that emacs throws some error that I don't understand. Or maybe the error says "Symbol's value as variable is void: modes", but there are many occurrences of the symbol modes in my code, so I need some context. Can Emacs be configured to mention the line number of the lisp code so I can know what code is causing the error?

I have tried doing (setq stack-trace-on-error '(buffer-read-only)) and ran the perpetrating code in an effort to get a stack trace. No stack trace either.

I have also tried calling edebug-defun on my function and stepping through it. It is not until I step out of the function that the error is thrown.

(I am really not as interested in the cause of the particular error I am facing currently as I am in developing general debugging skills for elisp. Please advise on how I can gleam a line number, or a sexp, or a stack trace from an error.)

4
  • Did you already try non-nil debug-on-error? Doesn't that help?
    – Drew
    Commented Feb 1, 2015 at 6:05
  • Nope. That doesn't seem to do anything. (After I set it to t and then proceed to eval an error-throwing function.) Commented Feb 1, 2015 at 6:26
  • Probably what happens is that some other code catches the error and just prints the error message. Also check that debug-ignored-errors doesn't list any errors. If you set debug-on-signal to non-nil, and it was the case the other code handled the error, you will be able to get the error before the other code did.
    – wvxvw
    Commented Feb 1, 2015 at 6:55
  • I am currently in a similar situation and was reading this question. I am wondering about stack-trace-on-error. This variable is not documented in Emacs 25.1.
    – Matthias
    Commented Feb 8, 2017 at 14:07

3 Answers 3

20

Emacs provides a good amount of debugging facilities including M-x toggle-debug-on-error, M-x toggle-debug-on-quit, debug on signal (which can be used by sending USR2 to Emacs from outside), debug-on-entry (of a function), debug-on-message (when seeing a specific regexp match of a message) and finally, debug itself as alternative to instrumenting a function with C-u C-M-x.

Both debug and edebug offer enough functionality to inspect the state of Emacs when evaluating the code that you're interested in, hit e and enter an expression.

However, while edebug jumps to the place in the instrumented function and therefore gives you a clue where to look (which is kind of silly since you already know what exactly you've instrumented), debug doesn't do this at all. I've pulled off a smaller hack after discovering that whenever debug evaluates a buffer, it emits the value of point associated with the error; in other words using this information on the buffer can give you a line number in the backtrace!

(with-eval-after-load 'debug
  (defun debugger-setup-buffer (debugger-args)
    "Initialize the `*Backtrace*' buffer for entry to the debugger.
That buffer should be current already."
    (setq buffer-read-only nil)
    (erase-buffer)
    (set-buffer-multibyte t)        ;Why was it nil ?  -stef
    (setq buffer-undo-list t)
    (let ((standard-output (current-buffer))
          (print-escape-newlines t)
          (print-level 8)
          (print-length 50))
      (backtrace))
    (goto-char (point-min))
    (delete-region (point)
                   (progn
                     (search-forward "\n  debug(")
                     (forward-line (if (eq (car debugger-args) 'debug)
                                       2    ; Remove implement-debug-on-entry frame.
                                     1))
                     (point)))
    (insert "Debugger entered")
    ;; lambda is for debug-on-call when a function call is next.
    ;; debug is for debug-on-entry function called.
    (pcase (car debugger-args)
      ((or `lambda `debug)
       (insert "--entering a function:\n"))
      ;; Exiting a function.
      (`exit
       (insert "--returning value: ")
       (setq debugger-value (nth 1 debugger-args))
       (prin1 debugger-value (current-buffer))
       (insert ?\n)
       (delete-char 1)
       (insert ? )
       (beginning-of-line))
      ;; Debugger entered for an error.
      (`error
       (insert "--Lisp error: ")
       (prin1 (nth 1 debugger-args) (current-buffer))
       (insert ?\n))
      ;; debug-on-call, when the next thing is an eval.
      (`t
       (insert "--beginning evaluation of function call form:\n"))
      ;; User calls debug directly.
      (_
       (insert ": ")
       (prin1 (if (eq (car debugger-args) 'nil)
                  (cdr debugger-args) debugger-args)
              (current-buffer))
       (insert ?\n)))
    ;; After any frame that uses eval-buffer,
    ;; insert a line that states the buffer position it's reading at.
    (save-excursion
      (let ((tem eval-buffer-list))
        (while (and tem
                    (re-search-forward "^  eval-\\(buffer\\|region\\)(" nil t))
          (beginning-of-line)
          (insert (format "Error at line %d in %s: "
                          (with-current-buffer (car tem)
                            (line-number-at-pos (point)))
                          (with-current-buffer (car tem)
                            (buffer-name))))
          (pop tem))))
    (debugger-make-xrefs)))

With this the original question in the title should be answered. As for your problem with getting a backtrace in the first place, I'm out of useful ideas.

4
  • Thanks for your help, but I still don't understand how to get the line number. M-x debug ...? Then what do I press? Commented Feb 7, 2015 at 21:56
  • With this code you'll see a line number in backtraces made by debug, you can check by visiting a faulty elisp file, doing M-x toggle-debug-on-error and M-x eval-buffer, then a backtrace with a line number at the problematic position should pop up.
    – wasamasa
    Commented Feb 7, 2015 at 23:37
  • Will this work if you don't use eval-buffer? For example, if you just press a keyboard shortcut that runs a private command that fails and opens the debugger in the *Backtrace* buffer.. Commented Mar 16, 2015 at 18:05
  • No, it won't. You get the function value of the symbol (which can either be a list or something byte-compiled) and that's pretty much it.
    – wasamasa
    Commented Mar 16, 2015 at 18:36
8

Maybe because it's 2018 now, but in my case, I only had to turn on debugging like wasamasa suggested: M-x toggle-debug-on-error

After this, M-x eval-buffer on my faulty Elisp file gave context by providing the position of the error, like this: Debugger entered--Lisp error: (invalid-read-syntax ")") eval-buffer() ; Reading at buffer position 523 [....]

M-x goto-char jumps to the error position: M-x goto-char 523

3
  • Nice find! It appears this has been added in 2017, back when they reworked that function to work on a list of backtrace items.
    – wasamasa
    Commented Sep 11, 2018 at 7:39
  • Additionally, it seems a button to jump to the location has been added.
    – wasamasa
    Commented Nov 20, 2022 at 12:06
  • Worked for locating an issue with a function that was added to kill-emacs-hook. The trace link didn't work as the function call is wrapped with a lambda function (is that the right way to say that?), but the combination of the actual error (void variable) and the function name gave me all I needed to know. Also in emacs 28.1, it's a menu item in MenuBar->Options->Enter Debugger on Error
    – Jim
    Commented Jan 11, 2023 at 15:54
1

I've extended wasamasa's answer to include additional information:

(save-excursion
  (let ((tem eval-buffer-list))
    (while (and tem
                (re-search-forward "^  eval-\\(buffer\\|region\\)(" nil t))
      (beginning-of-line)
      (insert (apply 'format "Error at line %d, column %d (point %d) in %s\n"
                     (with-current-buffer (car tem)
                       (list (line-number-at-pos (point))
                             (current-column)
                             (point)
                             (buffer-name)))))
      (pop tem))))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.