Search code examples
error-handlingclojurefunctional-programminglispclojurescript

How to access :cause, :via and :trace keys of an exception in Clojure?


I could not find a way to access :cause, :via and :trace keys of an exception.

Here is the code:

(try
  (throw (IllegalArgumentException. "1"))
  (catch Exception e
    e))

Output:

#error{:cause "1",
       :via [{:type java.lang.IllegalArgumentException, :message "1", :at [user$eval4073 invokeStatic "form-init5592296091748814678.clj" 1]}],
       :trace [[user$eval4073 invokeStatic "form-init5592296091748814678.clj" 1]
               [user$eval4073 invoke "form-init5592296091748814678.clj" 1]
               [clojure.lang.Compiler eval "Compiler.java" 6927]
               [clojure.lang.Compiler eval "Compiler.java" 6890]
               [clojure.core$eval invokeStatic "core.clj" 3105]
               [clojure.core$eval invoke "core.clj" 3101]
               [clojure.main$repl$read_eval_print__7408$fn__7411 invoke "main.clj" 240]
               ....]}

P.S: (:via e) does not work.


Solution

  • Clojure (the JVM) will throw a Java Exception object when an exception occurs. Clojure transforms that into data with the function Throwable->map and then prints that for you. You can call that function yourself:

    user=> (try (throw (Exception. "BOOM!")) 
             (catch Exception e 
               (Throwable->map e)))
    
    {:cause "BOOM!",
     :via [{:type java.lang.Exception,
            :message "BOOM!",
            :at [user$eval1 invokeStatic "NO_SOURCE_FILE" 1]}],
     :trace [[user$eval1 invokeStatic "NO_SOURCE_FILE" 1] 
             ...]}
    

    You can then just use normal keyword accessors on the returned data:

    user=> (println (:cause *1) (first (:via *1)))
    BOOM! {:type java.lang.Exception, :message BOOM!, :at [user$eval7 invokeStatic NO_SOURCE_FILE 4]}