Skip to content

Stacktraces and Error Reporting

Ramsey Nasser edited this page Dec 6, 2018 · 6 revisions

Exceptions thrown while executing Clojure code can have daunting stacktraces. The default string representations of these stacktraces are generated automatically by the CLR Exception class, and are therefore out of our hands. However, we do provide optional facilities to reformat how they print out in the REPL.

Unformatted, the Exception thrown by (/ 1 0) prints out like this:

user=> (/ 1 0)
System.Exception: carrier ---> System.ArithmeticException: Divide by zero
  at clojure.lang.Numbers.divide (System.Object x, System.Object y) [0x00024] in <138b9d0b0b3f4982b42abdb2b77eda11>:0 
  at clojure.lang.Numbers.divide (System.Int64 x, System.Int64 y) [0x00000] in <138b9d0b0b3f4982b42abdb2b77eda11>:0 
  at user$eval__94__99.invokeStatic () [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0 
  at user$eval__94__99.invoke () [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0 
  at clojure.lang.Compiler.eval (System.Object form) [0x001c2] in <138b9d0b0b3f4982b42abdb2b77eda11>:0 
  at clojure.lang.Compiler.eval (System.Object form) [0x00149] in <138b9d0b0b3f4982b42abdb2b77eda11>:0 
  at clojure$core$eval__22334.invokeStatic (System.Object ) [0x00000] in <0f47ef55b6284c9090d7b5b9ba53d099>:0 
  at clojure$core$eval__22334.invoke (System.Object ) [0x00000] in <0f47ef55b6284c9090d7b5b9ba53d099>:0 
  at arcadia$socket_repl$game_thread_evalfn__22511fn__22516__22528.invoke () [0x00011] in <a9b62fd8e501431b8ff830f23b301cf1>:0 
   --- End of inner exception stack trace ---
  at arcadia$socket_repl$game_thread_eval__22536.invokeStatic (System.Object , System.Object ) [0x0051c] in <a9b62fd8e501431b8ff830f23b301cf1>:0 
  at arcadia$socket_repl$game_thread_eval__22536.invoke (System.Object , System.Object ) [0x00000] in <a9b62fd8e501431b8ff830f23b301cf1>:0 
  at arcadia$socket_repl$game_thread_eval__22536.invokeStatic (System.Object ) [0x0000a] in <a9b62fd8e501431b8ff830f23b301cf1>:0 
  at arcadia$socket_repl$game_thread_eval__22536.invoke (System.Object ) [0x00000] in <a9b62fd8e501431b8ff830f23b301cf1>:0 
  at clojure.lang.Var.invoke (System.Object arg1) [0x00010] in <138b9d0b0b3f4982b42abdb2b77eda11>:0 
  at clojure$main$replread_eval_print__32312fn__32325__32329.invoke () [0x00000] in <401b91c732a5448f8964a5c487b9d869>:0 
  at clojure$main$replread_eval_print__32312__32332.invoke () [0x00124] in <401b91c732a5448f8964a5c487b9d869>:0 

With Arcadia's default formatting enabled, it prints like this:

user=> (/ 1 0)
System.ArithmeticException: Divide by zero
  clojure.lang.Numbers.divide (object, object)
  clojure.lang.Numbers.divide (System.Int64, System.Int64)
* user/eval
  clojure.lang.Compiler.eval (object)
  clojure.lang.Compiler.eval (object)
* clojure/core/eval
* arcadia/socket-repl/game-thread-evalfnfn

To take another example, say we have the following definitions:

(defn f5 [x y z]
  (throw (InvalidOperationException. "Exception from f5")))

(defn f4 [x y z]
  (f5 x y z))

(defn f3 [x y z]
  (f4 x y z))

(defn f2 [x y z]
  (f3 x y z))

(defn f1 [x y z]
  (f2 x y z))

With formatting turned off, (f1 :a :b :c) prints as follows:

user=> (f1 :a :b :c)
System.Exception: carrier ---> System.InvalidOperationException: Exception from f5
  at user$f5__105.invokeStatic (System.Object , System.Object , System.Object ) [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0 
  at user$f5__105.invoke (System.Object , System.Object , System.Object ) [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0 
  at user$f4__111.invokeStatic (System.Object , System.Object , System.Object ) [0x0000a] in <f57ab1e994bd43aa943a477771c387b2>:0 
  at user$f4__111.invoke (System.Object , System.Object , System.Object ) [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0 
  at user$f3__117.invokeStatic (System.Object , System.Object , System.Object ) [0x0000a] in <f57ab1e994bd43aa943a477771c387b2>:0 
  at user$f3__117.invoke (System.Object , System.Object , System.Object ) [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0 
  at user$f2__123.invokeStatic (System.Object , System.Object , System.Object ) [0x0000a] in <f57ab1e994bd43aa943a477771c387b2>:0 
  at user$f2__123.invoke (System.Object , System.Object , System.Object ) [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0 
  at user$f1__129.invokeStatic (System.Object , System.Object , System.Object ) [0x0000a] in <f57ab1e994bd43aa943a477771c387b2>:0 
  at user$f1__129.invoke (System.Object , System.Object , System.Object ) [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0 
  at user$eval__139__144.invokeStatic () [0x0000a] in <f57ab1e994bd43aa943a477771c387b2>:0 
  at user$eval__139__144.invoke () [0x00000] in <f57ab1e994bd43aa943a477771c387b2>:0 
  at clojure.lang.Compiler.eval (System.Object form) [0x001c2] in <138b9d0b0b3f4982b42abdb2b77eda11>:0 
  at clojure.lang.Compiler.eval (System.Object form) [0x00149] in <138b9d0b0b3f4982b42abdb2b77eda11>:0 
  at clojure$core$eval__22334.invokeStatic (System.Object ) [0x00000] in <0f47ef55b6284c9090d7b5b9ba53d099>:0 
  at clojure$core$eval__22334.invoke (System.Object ) [0x00000] in <0f47ef55b6284c9090d7b5b9ba53d099>:0 
  at arcadia$socket_repl$game_thread_evalfn__22511fn__22516__22528.invoke () [0x00011] in <a9b62fd8e501431b8ff830f23b301cf1>:0 
   --- End of inner exception stack trace ---
  at arcadia$socket_repl$game_thread_eval__22536.invokeStatic (System.Object , System.Object ) [0x0051c] in <a9b62fd8e501431b8ff830f23b301cf1>:0 
  at arcadia$socket_repl$game_thread_eval__22536.invoke (System.Object , System.Object ) [0x00000] in <a9b62fd8e501431b8ff830f23b301cf1>:0 
  at arcadia$socket_repl$game_thread_eval__22536.invokeStatic (System.Object ) [0x0000a] in <a9b62fd8e501431b8ff830f23b301cf1>:0 
  at arcadia$socket_repl$game_thread_eval__22536.invoke (System.Object ) [0x00000] in <a9b62fd8e501431b8ff830f23b301cf1>:0 
  at clojure.lang.Var.invoke (System.Object arg1) [0x00010] in <138b9d0b0b3f4982b42abdb2b77eda11>:0 
  at clojure$main$replread_eval_print__32312fn__32325__32329.invoke () [0x00000] in <401b91c732a5448f8964a5c487b9d869>:0 
  at clojure$main$replread_eval_print__32312__32332.invoke () [0x00124] in <401b91c732a5448f8964a5c487b9d869>:0 

With formatting turned on:

user=> (f1 :a :b :c)
System.InvalidOperationException: Exception from f5
* user/f5
* user/f4
* user/f3
* user/f2
* user/f1
* user/eval
  clojure.lang.Compiler.eval (object)
  clojure.lang.Compiler.eval (object)
* clojure/core/eval
* arcadia/socket-repl/game-thread-evalfnfn

Options

Formatting options are controlled by the :error-options key in Assets/configuration.edn, which should have a map value. This map will be merged into the following default map:

{:collapse-invocations  true,
 :demunge-classnames    true,
 :format                true, ;; if set to false, disables all formatting!
 :hide-outer-exceptions true,
 :star-ifn              true,

 :ditto-classnames      false,
 :hide-ifn-parameters   false,
 :hide-parameters       false,
 :quiet-invocations     false}

Descriptions of these options follow.

:collapse-invocations

Default value: true

Collapses consecutive stackframes corresponding to internal invocation methods of the same class implementing clojure.lang.IFn into a single line.

Internally, Clojure functions are instances of classes that implement the clojure.lang.IFn interface. Invocation is handled by methods such as invoke, invokeStatic, doInvoke, and applyTo, depending on context, often called in succession. This can greatly bloat stacktraces, and is rarely informative, since from the user's perspective the details of these internal method calls are usually irrelevant. :collapse-invocations will collapse these stackframes into a single line, and (for now) not display any parameters.

Note that this may obfuscate recursion.

:demunge-classnames

Default value: true

Types generated by the Clojure compiler (such as IFn classes generated by defn) often have names that are difficult to read, and that do not straightforwardly correspond to names they were associated with in code. :demunge-classnames formats these generated type names in a more legible manner.

:format

Default value: true

If set to false, disables all formatting. :format takes precedence over any other options.

:hide-outer-exceptions

Default value: true

If set to true, only the innermost exception will be displayed.

:star-ifn

Default value: true

If set to true, stackframes generated by IFn instances will be marked by an asterisk (*).

:ditto-classnames

Default value: false

If set to true, redundant classname information on successive lines of stacktraces will be omitted.

:hide-ifn-parameters

Default value: false

If set to true, the parameters of any stackframe the declaring class of which implements IFn will be hidden. These parameters will also be hidden if :collapse-invocations is true or :hide-parameters is true.

:hide-parameters

Default value: false

If set to true, the parameters of methods corresponding to stackframes will not be displayed.

:quiet-invocations

Default value: false

If set to true, the names of internal invocation methods such as invoke and invokeStatic for stackframes with declaring classes that implement IFn will not be displayed. These names will also not be displayed if :collapse-invocations is set to true.

Clone this wiki locally