Skip to content

Instantly share code, notes, and snippets.

@rafaeldff
Created December 30, 2019 16:57
Show Gist options
  • Save rafaeldff/bcef63d9418c4cb1e0fd6daad9df98ab to your computer and use it in GitHub Desktop.
Save rafaeldff/bcef63d9418c4cb1e0fd6daad9df98ab to your computer and use it in GitHub Desktop.
Clojure exceptions as data format

Clojure exceptions are really just Java exceptions under the hood. However, at times clojure shows them formatted as a map data structure. For instance, that's what you get if you inspect them in the REBL. This is because clojure implements the Datafy protocol for Throwable objects, delegating to clojure.core/Throwable->map.

This gist is a reminder of how to interpret that data assuming familiary with Java printed stack traces.

{:cause ; <- Exception message form the root (innermost) exception in the exception chain
"rootcause"
:data ; <- ex-data from the root (innermost) exception in the exception chain
{:data-from :a}
:trace ; <- stack trace of the root (innermost) exception in the exceptions chain
[[playground$a invokeStatic "playground.clj" 99] ; <- stack-frame that threw the exception is the first (topmost)
[playground$a invoke "playground.clj" 22]
[playground$b invokeStatic "NO_SOURCE_FILE" 102]
[playground$b invoke "NO_SOURCE_FILE" 101]
[playground$bar invokeStatic "NO_SOURCE_FILE" 105]
[playground$bar invoke "NO_SOURCE_FILE" 104]
[playground$foo invokeStatic "NO_SOURCE_FILE" 109]
[playground$foo invoke "NO_SOURCE_FILE" 108]
[playground$fn__24983 invokeStatic "playground.clj" 112]
[playground$fn__24983 invoke "playground.clj" 112]
[clojure.lang.AFn applyToHelper "AFn.java" 152]
[clojure.lang.AFn applyTo "AFn.java" 144]
[clojure.lang.Compiler$InvokeExpr eval "Compiler.java" 3702]
[clojure.lang.Compiler$DefExpr eval "Compiler.java" 457]
[clojure.lang.Compiler eval "Compiler.java" 7182]
[clojure.lang.Compiler eval "Compiler.java" 7132]
[clojure.core$eval invokeStatic "core.clj" 3214]
[clojure.core.server$prepl$fn__8941 invoke "server.clj" 232]
[clojure.core.server$prepl invokeStatic "server.clj" 228]
[clojure.core.server$prepl doInvoke "server.clj" 191]
[clojure.lang.RestFn invoke "RestFn.java" 425]
[cognitect.rebl$repl invokeStatic "rebl.clj" 122]
[cognitect.rebl$repl invoke "rebl.clj" 96]
[cognitect.rebl$_main invokeStatic "rebl.clj" 125]
[cognitect.rebl$_main invoke "rebl.clj" 124]
[clojure.lang.AFn applyToHelper "AFn.java" 152]
[clojure.lang.AFn applyTo "AFn.java" 144]
[clojure.lang.Var applyTo "Var.java" 705]
[clojure.core$apply invokeStatic "core.clj" 665]
[clojure.main$main_opt invokeStatic "main.clj" 514]
[clojure.main$main_opt invoke "main.clj" 510]
[clojure.main$main invokeStatic "main.clj" 664]
[clojure.main$main doInvoke "main.clj" 616]
[clojure.lang.RestFn applyTo "RestFn.java" 137]
[clojure.lang.Var applyTo "Var.java" 705]
[clojure.main main "main.java" 40]] ; <- stack-frame of the beggining of the thread call stack is the last (bottom)
:via ; <- for each exception in the chain:
[{:type clojure.lang.ExceptionInfo, ; <- It's class
:message "wrapping", ; <- It's message
:data {:data-from :bar}, ; <- It's ex-data
:at [playground$bar invokeStatic "NO_SOURCE_FILE" 106]} ; <- Stack frame that threw the exception (i.e. the topmost stack frame)
{:type clojure.lang.ExceptionInfo,
:message "rootcause",
:data {:data-from :a},
:at [playground$a invokeStatic "playground.clj" 99]}]
}
clojure.lang.ExceptionInfo: wrapping {:data-from :bar}
at playground$bar.invokeStatic(NO_SOURCE_FILE:106)
at playground$bar.invoke(NO_SOURCE_FILE:104)
at playground$foo.invokeStatic(NO_SOURCE_FILE:109)
at playground$foo.invoke(NO_SOURCE_FILE:108)
at playground$fn__24983.invokeStatic(playground.clj:112)
at playground$fn__24983.invoke(playground.clj:112)
at clojure.lang.AFn.applyToHelper(AFn.java:152)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3702)
at clojure.lang.Compiler$DefExpr.eval(Compiler.java:457)
at clojure.lang.Compiler.eval(Compiler.java:7182)
at clojure.lang.Compiler.eval(Compiler.java:7132)
at clojure.core$eval.invokeStatic(core.clj:3214)
at clojure.core.server$prepl$fn__8941.invoke(server.clj:232)
at clojure.core.server$prepl.invokeStatic(server.clj:228)
at clojure.core.server$prepl.doInvoke(server.clj:191)
at clojure.lang.RestFn.invoke(RestFn.java:425)
at cognitect.rebl$repl.invokeStatic(rebl.clj:122)
at cognitect.rebl$repl.invoke(rebl.clj:96)
at cognitect.rebl$_main.invokeStatic(rebl.clj:125)
at cognitect.rebl$_main.invoke(rebl.clj:124)
at clojure.lang.AFn.applyToHelper(AFn.java:152)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.lang.Var.applyTo(Var.java:705)
at clojure.core$apply.invokeStatic(core.clj:665)
at clojure.main$main_opt.invokeStatic(main.clj:514)
at clojure.main$main_opt.invoke(main.clj:510)
at clojure.main$main.invokeStatic(main.clj:664)
at clojure.main$main.doInvoke(main.clj:616)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.lang.Var.applyTo(Var.java:705)
at clojure.main.main(main.java:40)
Caused by: clojure.lang.ExceptionInfo: rootcause {:data-from :a}
at playground$a.invokeStatic(playground.clj:99)
at playground$a.invoke(playground.clj:22)
at playground$b.invokeStatic(NO_SOURCE_FILE:102)
at playground$b.invoke(NO_SOURCE_FILE:101)
at playground$bar.invokeStatic(NO_SOURCE_FILE:105)
... 31 more
(defn a []
(throw (ex-info "rootcause" {:data-from :a})))
(defn b []
(a))
(defn bar []
(try (b)
(catch Exception b (throw (ex-info "wrapping" {:data-from :bar} b)))))
(defn foo []
(bar))
(def e (try (foo) (catch Exception e e)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment