How do I get CIDER REPL error line numbers?

I’m trying to fix my CIDER setup. It’s been one of those nagging minor annoyances I haven’t been able to fix for a very long time

I edit my .clj files, fix functions, run little tests. Once some piece of code is (re)written I’m typically running cider-eval-defun-at-point or cider-eval-buffer (which I have bound to F-keys) and working through my problem interactively. Things get updated on the fly and on the whole it works kinda as expected.

The issue is that when I hit any error I get opaque stack traces that don’t specify line numbers in the file. Instead I get in-REPL errors - usually with unnamed lambdas. It makes hunting down the error location take longer than it should and can kinda breaks my flow

I can guess why… CIDER seems to be pushing the eval’d form into the REPL - so it’s original location is sort of lost. But is there some way to fix this? Or to work around it?

How do I stream line my workflow here?

PS: I tried to post about it on the CIDER Github but didn’t hear anything back: How do I get line numbers for expressions "sent" to the REPL · clojure-emacs/cider · Discussion #3328 · GitHub


Example stack trace

1. Unhandled java.lang.ClassCastException
   class clojure.lang.LazySeq cannot be cast to class java.lang.Number
   (clojure.lang.LazySeq is in unnamed module of loader 'app';
   java.lang.Number is in module java.base of loader 'bootstrap')

      175  clojure.lang.Numbers/multiply
                      REPL:  594  hw3/eval59084
                      REPL:  556  hw3/eval59084
    7194  clojure.lang.Compiler/eval
    7149  clojure.lang.Compiler/eval
                  core.clj: 3215  clojure.core/eval

This stuff that looks like REPL: 594 hw3/eval59084 doesn’t give you a spot to jump to

The Cider docs describe the Cider tools to navigate the stack trace it displays, providing tools to focus on specific aspects of the error. Perhaps these stack trace tools may help.

cider-stacktrace-jump should in most cases jump to the location in source code where the error occurred. There many be some limitations if the error occurred in a Java library and the Java sources are not configured for CIDER (although looking at the Java code may not be that useful anyway)

Where there are unnamed functions in the stack trace, some mitigation can be made by defining a name when anonymous functions are used (fn name [args] ,,,) rather than (fn [args] ,,,)

I should say upfront that hopefully you will get a much better answer from someone who works on CIDER or Clojure! I’m just a user of both who pokes around at things, so take what I say with appropriate doses of salt. You also might have better luck in a CIDER-focused stream on the Clojurians Zulip.

To answer questions like this, it really helps to dig into the Clojure source, and think about things like “how does the runtime even know what line and column numbers to associate with an exception when it is thrown in the first place?” There are lots of pieces to the answer, but an important one is that when Clojure is reading expressions using read that it is going to compile using eval, it uses a LineNumberingPushbackReader to keep track of the lines and columns of the file it is parsing, and (for example) here is where the LispReader class seems to attach that information as metadata to a list that it has just read. Then when eval is generating the byte code that the list compiles into, it can attach the line and column information to the compiled code in the format that the JVM consults when exceptions get thrown.

So when a tool like CIDER tells Clojure to recompile an entire file, there is a pretty clear answer to how it will be possible to give you nice line and column information when exceptions are thrown.

But what should happen when you are just evaluating an isolated expression? Well, maybe there would be a way for CIDER in coordination with its nrepl middleware to pre-initialize a LineNumberingPushbackReader so that it would know the line number in the file that your chunk of code begins at, and pass that in to the Clojure read/eval process. If this would be valuable enough for you, this might be an MR that you could consider trying to contribute yourself. But my own feeling is that it is not valuable enough that I would want to try it. Once you start editing and compiling pieces of a file, the line numbers quickly become meaningless. If you add or remove lines, the trick I am describing could make them correct for the chunk of code you just compiled, but all code that lives after that in the file would suddenly have incorrect line numbers attached to it, because it was compiled before you added or removed lines.

So if you are working in such a way that you want the line numbers to be right, so you can do things like jumping to the source of the error, the easy and completely correct way to achieve that is to simply always recompile the entire file.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.