Fork me on GitHub
#clojurescript
<
2023-10-18
>
Lidor Cohen10:10:21

Hello everyone 👋 How does the REPL pass\handle exceptions over evaluated expressions? I ask because I encountered an unexpected behavior when I wrapped an exception with more info along the chain but when I evaluate an expression I still seem to get only the inner most exception. for illustration of the gap:

(-> my-obj
    -resolve)
=>
:repl/exception!
; 
; Execution error (Error) at (<cljs repl>:1).
; No protocol method ILookup.-lookup defined for type null: 

(try (-> my-obj
         -resolve)
     (catch js/Object e
       e))
=>
#error {:message "My Exception"
        :cause #object[Error Error: No protocol method ILookup.-lookup defined for type null: ]}
not sure if it's relevant but I construct and throw my exception using (throw (ex-info...))

thheller11:10:28

kinda hard to answer, as this doesn't show anything about the code actually throwing the exception.

thheller11:10:53

each repl eval has an implicit try/catch, so the first one the REPL eval is printing the caught exception

thheller11:10:05

in the second it is just printing the return value, as a regular value

Lidor Cohen11:10:35

> each repl eval has an implicit try/catch, so the first one the REPL eval is printing the caught exception "the first one the REPL eval" meaning the top form? in this case:

(-> my-obj
    -resolve)
? if that's the case i'd expect that the REPL and the expr:
(try (-> my-obj
         -resolve)
     (catch js/Object e
       e))
will return (or print) the same thing because it sounds that the repl's implicit try/catch of:
(-> my-obj
    -resolve)
will do something very similar to this:
(try (-> my-obj
         -resolve)
     (catch js/Object e
       e))

thheller17:10:02

it isn't. it is more like (try (expr) (catch :default e (prn e) :repl/exception!))

thheller17:10:22

REPLs bind any caught error to *e by default

Lidor Cohen20:10:00

so would you say evaluating this expression:

(try (-> my-obj
         -resolve)
     (catch :default e (prn e)
            :repl/exception!))
should result about the same as just evaluating this expression:
(-> my-obj
    -resolve)
?

Lidor Cohen20:10:57

PS the "full" exception (my exception) indeed appears in *e after evaluation. do you know why the repl doesn't print it and instead print only the inner most exception?

thheller06:10:19

sorry but this is absolutely impossible to answer without knowing what your code is actually doing. *e is bound the the actual (catch :default e) e exception here. there is no unwrapping or whatever done of any kind, it is also the exact same that is getting printed.

thheller06:10:43

it is not actual prn that is getting used, so no it is not the same.

thheller06:10:46

basically in a CLJS REPL there are many involved things happening. there is a lot of async IO and your editor is involved too when using emacs for example

thheller06:10:10

each part can decide to print the REPL error differently

Lidor Cohen06:10:22

But *e is not what gets printed. And I'm asking about the general handling of exceptions in the REPL, how can my code affect it? If you're asserting that *e is what should get printed, then I have a curious case here, because it is not. If e is not what gets printed then my question is what *is get printed (in the general manner).

Lidor Cohen06:10:04

Sorry your on going answer is great

Lidor Cohen06:10:33

So your saying that, in my case, calva also has a say

thheller06:10:33

*e is the caught exception

thheller06:10:28

so I'm not entirely sure what you are after. exceptions in JS do not behave like JVM/CLJ exceptions, since there is no inherent "cause" exception. and there is no re-throwing of exceptions.

Lidor Cohen06:10:28

Maybe some component along the way affects the printing

thheller06:10:20

again ... I cannot give you any more specific answers without knowing the code that is actually throwing the exception

thheller06:10:06

oh and things get even more fun it the exception is part of a react render phase, since you said component I assume that is what you meant

thheller06:10:50

because then likely the whole thing is getting caught by a react error boundary, so your catch might not actually be the thing catching anything

thheller06:10:15

if you are using the latest shadow-cljs version the exception is also always printed to the browser console, since sometimes that is the best way to see an actual error with proper source mapping and stuff

thheller06:10:40

there was also a change in 2.25.7 affecting REPL errors in general, so maybe if you are not on that version the old behavior was what confuses you? https://github.com/thheller/shadow-cljs/compare/a08f16f8b7b5e929959f797c23384f47fd545a1c...9cc5e7830ceea6adcd814ec551526f13291431a5

thheller06:10:55

generally it should be a bit cleaner now, but *e was always the caught exception and what got printed, so no change there

Lidor Cohen06:10:44

I'm not coming with some jvm assumptions because I don't have deep knowledge on how exceptions are handled in the jvm. I meant components in the general sense as a part of the process that prints the exception, no react involved in this phase. My question is quite naive: I construct a detailed error along the execution path but for some reason (that reason is what I'm after) what's printed is corresponding to the error I construct (or what stored in e, which *is the error I constructed). My question is: why I throw one exception and the repl prints another? Is that a normal thing?

Lidor Cohen06:10:29

> generally it should be a bit cleaner now, but *e was always the caught exception and what got printed, so no change there > This is not what happens in my case e does *not get printed, hence my question...

Lidor Cohen06:10:19

But if you are certain that *e is what's get printed, then it might be that some component along the tool chain (shadow, calva...) Digs recursively into :cause because if you'd do that to my error you'd get what is printed in the calva repl.

thheller06:10:38

definitely upgrade shadow-cljs if you are not on 2.25.7+, maybe that solves your question

Lidor Cohen06:10:35

Thanks, I upgraded to 2.25.8 and that printed the exception to the console. so the situation is this: *e - correct exception console - correct exception output.calva-repl - not full exception (either deepest cause or first exception that been thrown) I'll keep investigating on calva-repl error printing. thanks @U05224H0W for your time and patience