2

I'm working on my shopping app and I'm trying to make a custom exception handler, but there's something else that's catching them before I get to them!

Here is my handler

(defn wrap-fallback-exception
    [handler]
    (fn [request]
        (try+
            (handler request)
            (catch [:type :test] {:keys [msg]}
                (println "## Test Exception:" msg))
            (catch Exception e (println "## Exception"))
            (catch Throwable e
                (println "## Throwable:" (.getMessage e) e)))))

Here is my stack

(-> all-routes
        (wrap-anti-forgery)
        (friend/authenticate {
             :unauthorized-handler unauth-handler
             :credential-fn        (partial cred get-user)
             :workflows            [(workflows/interactive-form)]})
        (wrap-session {:store (->ShopStore )})
        (wrap-keyword-params)
        (wrap-params)
        (wrap-cookies)
        (wrap-fallback-exception)
        (run-server {:port 3000}))

I trow a test in one of my ring functions

(throw+ (ex-info "TEST" {:type :test}))

And the resulting output looks like this

Sun Apr 01 15:57:29 CEST 2018 [worker-4] ERROR - GET /user/home
clojure.lang.ExceptionInfo: TEST {:type :test}
    at clojure.core$ex_info.invokeStatic(core.clj:4739)
    at clojure.core$ex_info.invoke(core.clj:4739)
    at shop2.views.home$mk_proj_row.invokeStatic(home.clj:90)
    at shop2.views.home$mk_proj_row.invoke(home.clj:88)
    at clojure.lang.AFn.applyToHelper(AFn.java:154)
    at clojure.lang.AFn.applyTo(AFn.java:144)
    at orchestra.spec.test$spec_checking_fn$fn__1372.doInvoke(test.clj:123)
    at clojure.lang.RestFn.invoke(RestFn.java:408)
    at clojure.core$map$fn__5587.invoke(core.clj:2747)
    at clojure.lang.LazySeq.sval(LazySeq.java:40)
    at clojure.lang.LazySeq.seq(LazySeq.java:49)
    at clojure.lang.RT.seq(RT.java:528)
    at clojure.core$seq__5124.invokeStatic(core.clj:137)
    at clojure.core$print_sequential.invokeStatic(core_print.clj:53)
    at clojure.core$fn__7019.invokeStatic(core_print.clj:174)
    at clojure.core$fn__7019.invoke(core_print.clj:174)
    at clojure.lang.MultiFn.invoke(MultiFn.java:233)
    at clojure.core$pr_on.invokeStatic(core.clj:3666)
    at clojure.core$pr_on.invoke(core.clj:3660)
    at clojure.core$print_sequential.invokeStatic(core_print.clj:66)
    at clojure.core$fn__7039.invokeStatic(core_print.clj:225)
    at clojure.core$fn__7039.invoke(core_print.clj:225)
    at clojure.lang.MultiFn.invoke(MultiFn.java:233)
    at clojure.core$pr_on.invokeStatic(core.clj:3666)
    at clojure.core$pr_on.invoke(core.clj:3660)
    at clojure.core$print_sequential.invokeStatic(core_print.clj:66)
    at clojure.core$fn__7039.invokeStatic(core_print.clj:225)
    at clojure.core$fn__7039.invoke(core_print.clj:225)
    at clojure.lang.MultiFn.invoke(MultiFn.java:233)
    at clojure.core$pr_on.invokeStatic(core.clj:3666)
    at clojure.core$pr_on.invoke(core.clj:3660)
    at clojure.core$print_sequential.invokeStatic(core_print.clj:66)
    at clojure.core$fn__7039.invokeStatic(core_print.clj:225)
    at clojure.core$fn__7039.invoke(core_print.clj:225)
    at clojure.lang.MultiFn.invoke(MultiFn.java:233)
    at clojure.core$pr_on.invokeStatic(core.clj:3666)
    at clojure.core$pr_on.invoke(core.clj:3660)
    at clojure.core$print_sequential.invokeStatic(core_print.clj:66)
    at clojure.core$fn__7019.invokeStatic(core_print.clj:174)
    at clojure.core$fn__7019.invoke(core_print.clj:174)
    at clojure.lang.MultiFn.invoke(MultiFn.java:233)
    at clojure.core$pr_on.invokeStatic(core.clj:3666)
    at clojure.core$pr_on.invoke(core.clj:3660)
    at clojure.core$print_sequential.invokeStatic(core_print.clj:66)
    at clojure.core$fn__7019.invokeStatic(core_print.clj:174)
    at clojure.core$fn__7019.invoke(core_print.clj:174)
    at clojure.lang.MultiFn.invoke(MultiFn.java:233)
    at clojure.core$pr_on.invokeStatic(core.clj:3666)
    at clojure.core$pr_on.invoke(core.clj:3660)
    at clojure.core$print_sequential.invokeStatic(core_print.clj:66)
    at clojure.core$fn__7039.invokeStatic(core_print.clj:225)
    at clojure.core$fn__7039.invoke(core_print.clj:225)
    at clojure.lang.MultiFn.invoke(MultiFn.java:233)
    at clojure.core$pr_on.invokeStatic(core.clj:3666)
    at clojure.core$pr_on.invoke(core.clj:3660)
    at clojure.lang.Var.invoke(Var.java:385)
    at clojure.lang.RT.print(RT.java:1877)
    at clojure.lang.RT.printString(RT.java:1857)
    at clojure.lang.APersistentVector.toString(APersistentVector.java:26)
    at org.httpkit.HttpUtils.bodyBuffer(HttpUtils.java:112)
    at org.httpkit.HttpUtils.HttpEncode(HttpUtils.java:442)
    at org.httpkit.server.HttpHandler.run(RingHandler.java:101)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

The exception is never caught by my handler! I've tried every version I can think of with no luck :-( Who is catching it? And how do I make it stop?

Regards Soren

Kingfranz
  • 184
  • 1
  • 12

1 Answers1

1

Your handler returns successfully, without throwing an exception. The value that it returns is, apparently, a many-levels-deep vector containing, buried deep within itself, a lazy sequence. When Ring tries to print that lazy sequence, forcing that sequence calls mk-proj-row, which throws an exception. Importantly, this happens after your handler has completely finished running, meaning the exception handler is no longer in effect.

I doubt you really want to be stringifying a giant vector as your output: my guess is you forgot to use Hiccup to convert that to HTML, or something else to convert it to JSON. You should probably have some middleware installed which does that conversion before returning to Ring; if you put that middleware before your wrap-fallback-exception middleware, then the laziness will no longer be a problem, because the lazy sequence will be realized inside of the Hiccup (or whatever) renderer, rather than by Ring after all your handlers have finished.

amalloy
  • 89,153
  • 8
  • 140
  • 205