Reading and understanding Clojure errors

Tips to debug Clojure reading errors and stacktraces

Let's start with a simple one:
;; A simple function that always throw an exception
(defn my-throw-f [x]
  (throw (ex-info (str "Invalid x: " x)
                  {:x x})))
=> #'user/my-throw-f
;; Let's call it
(my-throw-f "hello")
Execution error (ExceptionInfo) at user/my-throw-f (REPL:2).
Invalid x: hello
What I can understand from this error? Let's split it into blocks: Now let's try an exception that was not created by us
;; Calling my-throw-f, that need 1 argument, with zero args.
Execution error (ArityException) at user/eval137 (REPL:1).
Wrong number of args (0) passed to: user/my-throw-f
Let's see the blocks: Now let's do a trick: create an anonymous function with a name and immediately invoke it.
((fn my-context []
Execution error (ArityException) at user/eval139$my-context (REPL:2).
Wrong number of args (0) passed to: user/my-throw-f
The context goes from user/eval137, that means basically nothing to user/eval139$my-context that IMHO is way easier to find. Let's use it to read our first stacktrace!
#error {
 :cause "Wrong number of args (0) passed to: user/my-throw-f"
 [{:type clojure.lang.ArityException
   :message "Wrong number of args (0) passed to: user/my-throw-f"
   :at [clojure.lang.AFn throwArity "" 429]}]
 [[clojure.lang.AFn throwArity "" 429]
  [clojure.lang.AFn invoke "" 28]
  [user$eval139$my_context__140 invoke "NO_SOURCE_FILE" 2]

Once we created a context called my-context to our execution, anything before this context should not be relevant. Now let's try a harder one: an lazy stacktrace.
(map my-throw-f [1])
Error printing return value (ExceptionInfo) at user/my-throw-f (NO_SOURCE_FILE:2).
Invalid x: 1
Here a new thing: Error printing return value. it's not a Execution error anymore. The code was executed, and returns a value. clojure.core/map returns a lazy-seq, and when the printer tryies to print the result, it throws. Let's see the stacktrace
#error {
 :cause "Invalid x: 1"
 :data {:x 1}
 [{:type clojure.lang.ExceptionInfo
   :message nil
   :data #:clojure.error{:phase :print-eval-result}
   :at [clojure.main$repl$read_eval_print__9112 invoke "main.clj" 442]}
  {:type clojure.lang.ExceptionInfo
   :message "Invalid x: 1"
   :data {:x 1}
   :at [user$my_throw_f invokeStatic "NO_SOURCE_FILE" 2]}]
 [[user$my_throw_f invokeStatic "NO_SOURCE_FILE" 2]
  [user$my_throw_f invoke "NO_SOURCE_FILE" 1]                      ;; [6]
  [clojure.core$map$fn__5885 invoke "core.clj" 2757]               ;; [5]
  [clojure.lang.LazySeq sval "" 42]
  [clojure.lang.LazySeq seq "" 51]                     ;; [4]
  [clojure.lang.RT seq "" 535]
  [clojure.core$seq__5420 invokeStatic "core.clj" 139]
  [clojure.core$print_sequential invokeStatic "core_print.clj" 53]
  [clojure.core$fn__7331 invokeStatic "core_print.clj" 174]
  [clojure.core$fn__7331 invoke "core_print.clj" 174]
  [clojure.lang.MultiFn invoke "" 234]                 ;; [3]
  [clojure.core$pr_on invokeStatic "core.clj" 3662]
  [clojure.core$pr invokeStatic "core.clj" 3665]
  [clojure.core$pr invoke "core.clj" 3665]
  [clojure.lang.AFn applyToHelper "" 154]
  [clojure.lang.RestFn applyTo "" 132]
  [clojure.core$apply invokeStatic "core.clj" 667]
  [clojure.core$prn invokeStatic "core.clj" 3702]
  [clojure.core$prn doInvoke "core.clj" 3702]                      ;; [2]
  [clojure.lang.RestFn invoke "" 408]
  [clojure.main$repl$read_eval_print__9112 invoke "main.clj" 442]  ;; [1]
  [clojure.main$repl$fn__9121 invoke "main.clj" 458]
  [clojure.main$repl invokeStatic "main.clj" 458]
  [clojure.main$repl_opt invokeStatic "main.clj" 522]
  [clojure.main$main invokeStatic "main.clj" 667]
  [clojure.main$main doInvoke "main.clj" 616]
  [clojure.lang.RestFn invoke "" 397]
  [clojure.lang.AFn applyToHelper "" 152]
  [clojure.lang.RestFn applyTo "" 132]
  [clojure.lang.Var applyTo "" 705]
  [clojure.main main "" 40]]}
I highlight some points of this stacktrace: Extra tips: