clerk 2025-05-06

Hi, I want to do a "side-effectful" call to load some messages from kafka into a clerk notebook, similar to what's explained in the book under section Cached Evaluation. My code looks like this:

^{::clerk/visibility {:code :show :result :hide}}
(def msgs
  (let [_run-at #inst "2025-05-06T07:16:47.060791288Z"]
    (-kafka-utils/load-messages-from-confluent-topic :bootstrap-server (:kafka-bootstrap-server cfg)
                                                     :api-key           (:confluent-api-key cfg)
                                                     :api-secret        (:confluent-api-secret cfg)
                                                     :topics            ["v2-update-product"]
                                                     :extra-strategies  [(-kafka/StringSerializer :consumer :key)
                                                                         (-kafka/EdnSerializer :consumer :value)
                                                                         (-kafka/SeekToTimestampOffset msgs-when-to-start)
                                                                         ]
                                                     :collect-messages? true
                                                     :nr-msgs           1000)))


(first msgs)
This code works in the repl, but in the clerk window I'm getting:
Unhandled clojure.lang.ExceptionInfo
Cannot find field parseLong for class class java.lang.Long
{:class java.lang.Long 
 :column 60 
 :end-column 99 
 :end-line 528 
 :field parseLong 
 :file "NO_SOURCE_PATH"  
 :line 15}

Can you paste more of the stacktrace (e.g. is it coming from tools analyzer) and can you try clerk from the main branch last commit? We switched out the analyzer for a custom home-built one

The stacktrace is quite long, I didn't want to lead with that. This below looks like it's recursed deep into PersistentVector.java: 418

analyze_host_expr.clj:	73	clojure.tools.analyzer.passes.jvm.analyze-host-expr/analyze-host-field
analyze_host_expr.clj:	68	clojure.tools.analyzer.passes.jvm.analyze-host-expr/analyze-host-field
analyze_host_expr.clj:	172	clojure.tools.analyzer.passes.jvm.analyze-host-expr/analyze-host-expr
analyze_host_expr.clj:	142	clojure.tools.analyzer.passes.jvm.analyze-host-expr/analyze-host-expr
Var.java:	386	clojure.lang.Var
passes.clj:	167	clojure.tools.analyzer.passes/compile-passes/fn--8852/fn--8857
passes.clj:	169	clojure.tools.analyzer.passes/compile-passes/fn--8852/fn--8859
passes.clj:	169	clojure.tools.analyzer.passes/compile-passes/fn--8852/fn--8859
passes.clj:	169	clojure.tools.analyzer.passes/compile-passes/fn--8852/fn--8859
passes.clj:	169	clojure.tools.analyzer.passes/compile-passes/fn--8852/fn--8859
passes.clj:	169	clojure.tools.analyzer.passes/compile-passes/fn--8852/fn--8859
passes.clj:	169	clojure.tools.analyzer.passes/compile-passes/fn--8852/fn--8859
passes.clj:	169	clojure.tools.analyzer.passes/compile-passes/fn--8852/fn--8859
core.clj:	2648	clojure.core/partial/fn--5929
ast.clj:	102	clojure.tools.analyzer.ast/walk/walk--8741
ast.clj:	96	clojure.tools.analyzer.ast/walk/walk--8741/walk--8742
utils.clj:	208	clojure.tools.analyzer.utils/mapv'
utils.clj:	202	clojure.tools.analyzer.utils/mapv'
ast.clj:	51	clojure.tools.analyzer.ast/-update-children/fn--8730
PersistentVector.java:	418	clojure.lang.PersistentVector
core.clj:	6964	clojure.core/reduce
core.clj:	6947	clojure.core/reduce
ast.clj:	49	clojure.tools.analyzer.ast/-update-children
ast.clj:	46	clojure.tools.analyzer.ast/-update-children
ast.clj:	64	clojure.tools.analyzer.ast/update-children-reduced
ast.clj:	58	clojure.tools.analyzer.ast/update-children-reduced
ast.clj:	99	clojure.tools.analyzer.ast/walk/walk--8741
ast.clj:	96	clojure.tools.analyzer.ast/walk/walk--8741/walk--8742
ast.clj:	51	clojure.tools.analyzer.ast/-update-children/fn--8730
...
Bottom of the stack:
...
PersistentVector.java:	418	clojure.lang.PersistentVector
core.clj:	6964	clojure.core/reduce
core.clj:	6947	clojure.core/reduce
ast.clj:	49	clojure.tools.analyzer.ast/-update-children
ast.clj:	46	clojure.tools.analyzer.ast/-update-children
ast.clj:	64	clojure.tools.analyzer.ast/update-children-reduced
ast.clj:	58	clojure.tools.analyzer.ast/update-children-reduced
ast.clj:	99	clojure.tools.analyzer.ast/walk/walk--8741
ast.clj:	96	clojure.tools.analyzer.ast/walk/walk--8741/walk--8742
ast.clj:	51	clojure.tools.analyzer.ast/-update-children/fn--8730
PersistentVector.java:	418	clojure.lang.PersistentVector
core.clj:	6964	clojure.core/reduce
core.clj:	6947	clojure.core/reduce
ast.clj:	49	clojure.tools.analyzer.ast/-update-children
ast.clj:	46	clojure.tools.analyzer.ast/-update-children
ast.clj:	64	clojure.tools.analyzer.ast/update-children-reduced
ast.clj:	58	clojure.tools.analyzer.ast/update-children-reduced
ast.clj:	99	clojure.tools.analyzer.ast/walk/walk--8741
ast.clj:	95	clojure.tools.analyzer.ast/walk
ast.clj:	84	clojure.tools.analyzer.ast/walk
ast.clj:	115	clojure.tools.analyzer.ast/postwalk
ast.clj:	110	clojure.tools.analyzer.ast/postwalk
ast.clj:	113	clojure.tools.analyzer.ast/postwalk
ast.clj:	110	clojure.tools.analyzer.ast/postwalk
passes.clj:	171	clojure.tools.analyzer.passes/compile-passes/analyze--8864
core.clj:	2586	clojure.core/comp/fn--5895
core.clj:	2586	clojure.core/comp/fn--5895
jvm.clj:	476	clojure.tools.analyzer.jvm/run-passes
jvm.clj:	468	clojure.tools.analyzer.jvm/run-passes
jvm.clj:	519	clojure.tools.analyzer.jvm/analyze/fn--11408/fn--11413
jvm.clj:	517	clojure.tools.analyzer.jvm/analyze/fn--11408
AFn.java:	152	clojure.lang.AFn
AFn.java:	144	clojure.lang.AFn
core.clj:	667	clojure.core/apply
core.clj:	1990	clojure.core/with-bindings*
core.clj:	1990	clojure.core/with-bindings*
RestFn.java:	428	clojure.lang.RestFn
jvm.clj:	506	clojure.tools.analyzer.jvm/analyze
jvm.clj:	486	clojure.tools.analyzer.jvm/analyze
analyzer.clj:	138	nextjournal.clerk.analyzer/analyze-form/fn--37043
core.clj:	7706	clojure.core/with-redefs-fn
core.clj:	7690	clojure.core/with-redefs-fn
analyzer.clj:	135	nextjournal.clerk.analyzer/analyze-form
analyzer.clj:	128	nextjournal.clerk.analyzer/analyze-form
analyzer.clj:	172	nextjournal.clerk.analyzer/analyze
analyzer.clj:	164	nextjournal.clerk.analyzer/analyze
analyzer.clj:	397	nextjournal.clerk.analyzer/analyze-doc/fn--37167
PersistentVector.java:	418	clojure.lang.PersistentVector
core.clj:	6964	clojure.core/reduce
core.clj:	6947	clojure.core/reduce
analyzer.clj:	381	nextjournal.clerk.analyzer/analyze-doc
analyzer.clj:	374	nextjournal.clerk.analyzer/analyze-doc
analyzer.clj:	442	nextjournal.clerk.analyzer/analyze-file
analyzer.clj:	436	nextjournal.clerk.analyzer/analyze-file
analyzer.clj:	598	nextjournal.clerk.analyzer/build-graph/fn--37251
protocols.clj:	42	clojure.core.protocols/iterator-reduce!
protocols.clj:	52	clojure.core.protocols/iter-reduce
protocols.clj:	74	clojure.core.protocols/fn--8256
protocols.clj:	74	clojure.core.protocols/fn--8256
protocols.clj:	13	clojure.core.protocols/fn--8203/G--8198--8216
core.clj:	6965	clojure.core/reduce
core.clj:	6947	clojure.core/reduce
analyzer.clj:	585	nextjournal.clerk.analyzer/build-graph
analyzer.clj:	568	nextjournal.clerk.analyzer/build-graph
eval.clj:	271	nextjournal.clerk.eval/+eval-results
eval.clj:	263	nextjournal.clerk.eval/+eval-results
clerk.clj:	72	nextjournal.clerk/show!/fn--41728
clerk.clj:	71	nextjournal.clerk/show!
clerk.clj:	22	nextjournal.clerk/show!
clerk.clj:	143	nextjournal.clerk/file-event
clerk.clj:	135	nextjournal.clerk/file-event
clerk.clj:	509	nextjournal.clerk/serve!/fn--41811
beholder.clj:	13	nextjournal.beholder/fn/reify--31727
DirectoryWatcher.java:	402	io.methvin.watcher.DirectoryWatcher
DirectoryWatcher.java:	349	io.methvin.watcher.DirectoryWatcher
DirectoryWatcher.java:	232	io.methvin.watcher.DirectoryWatcher
CompletableFuture.java:	1768	java.util.concurrent.CompletableFuture/AsyncSupply
CompletableFuture.java:	1760	java.util.concurrent.CompletableFuture/AsyncSupply
ForkJoinTask.java:	387	java.util.concurrent.ForkJoinTask
ForkJoinPool.java:	1312	java.util.concurrent.ForkJoinPool/WorkQueue
ForkJoinPool.java:	1843	java.util.concurrent.ForkJoinPool
ForkJoinPool.java:	1808	java.util.concurrent.ForkJoinPool
ForkJoinWorkerThread.java:	188	java.util.concurrent.ForkJoinWorkerThread

I'll try with main and revert...

It also breaks with main branch from clerk. I think it has to do with how -kafka-utils/load-messages-from-confluent-topic is implemented. https://github.com/Afrolabs/afrolabs-clj/blob/5222f9cade48491906744f5c924749f6c69b7803/src/afrolabs/components/kafka/utilities.clj#L34.

Unhandled clojure.lang.ExceptionInfo
Could not resolve var: msgs
{:column 93 :file "NO_SOURCE_PATH"
 :line 35 :var msgs}

The stacktrace looks extremely similar.

If the stacktrace looks similar you don't have the main branch perhaps, since we don't use tools.analyzer in there anymore

can you maybe also post the stacktrace from the main branch?

or even better a full repro including deps.edn and standalone notebook that I can run locally

Not a solution to your problem (analyzer errors), but sharing how I'm working with "providing data" into Clerk notebooks as it can ensure a smooth Clerk experience by separating data generation Clerk analysis, and ensure quick Clerk feedback loops. — I do the following when I read data into Clerk from somewhere outside of the Clerk cache: 1. Push new data into some place outside of Clerk 2. Do a ^::clerk/no-cache (def new-analysis-timestamp (load-analysis-timestamp-from-local-data-outside-of-clerk-cache)) 3. Then define Clerk-controlled defs based on new-analysis-timestamp. As long as load-analysis-timestamp-from-local-data-outside-of-clerk-cache returns quickly, I can play nicely with Clerk's caching and get a quick feedback loop.

Thank you Teodor, that's maybe addressing the issue I really have. My "load data from kafka" takes loooooong. Minutes, sometimes 10's of minutes. I think I'll manually create a json file, and then slurp in a way you suggest.

💯 1
❤️ 1

@pieterbreed Still would like to have the longer stacktrace and/or repro so the analyzer that's not been released could possibly be improved :)

➕ 1

Thank you @borkdude, I'll respond within hours 👍🏽 🙂

👍 thanks

Hmm, when I try to add that library via add-lib I'm getting:

Could not find artifact org.apache.kafka:kafka-clients:jar:7.9.0-ce in
   central ()
   {:via [{:type clojure.lang.ExceptionInfo, :message "Could not find artifact org.apache.kafka:kafka-clients:jar:7.9.0-ce in central ()", :data {:lib org.apache.kafka/kafka-clients, :coord {:mvn/version "7.9.0-ce", :deps/manifest :mvn, :dependents [afrolabs/afrolabs-clj], :parents #{[afrolabs/afrolabs-clj]}}}, :at [clojure.tools.deps.extensions.maven$get_artifact invokeStatic "maven.clj" 167]}], :trace [[clojure.tools.deps.extensions.maven$get_artifact invokeStatic "maven.clj" 167] [clojure.tools.deps.extensions.maven$get_artifact invoke "maven.clj" 155] [clojure.tools.deps.extensions.maven$eval1225$fn__1228 invoke "maven.clj" 178] [clojure.lang.MultiFn invoke "MultiFn.java" 244] [clojure.tools.deps$download_libs$fn__802$fn__803 invoke "deps.clj" 466] ...], :cause "Could not find artifact org.apache.kafka:kafka-clients:jar:7.9.0-ce in central ()", :data {:lib org.apache.kafka/kafka-clients, :coord {:mvn/version "7.9.0-ce", :deps/manifest :mvn, :dependents [afrolabs/afrolabs-clj], :parents #{[afrolabs/afrolabs-clj]}}}}
               interop.clj:   81  clojure.tools.deps.interop/invoke-tool
               interop.clj:   41  clojure.tools.deps.interop/invoke-tool
                  deps.clj:   48  clojure.repl.deps/add-libs
                  deps.clj:   59  clojure.repl.deps/add-lib
                  deps.clj:   59  clojure.repl.deps/add-lib
                      REPL:    1  user/eval60501
                      REPL:    1  user/eval60501
             Compiler.java: 7700  clojure.lang.Compiler/eval
    interruptible_eval.clj:  106  nrepl.middleware.interruptible-eval/evaluator/run/fn
    interruptible_eval.clj:  101  nrepl.middleware.interruptible-eval/evaluator/run
               session.clj:  230  nrepl.middleware.session/session-exec/session-loop
        SessionThread.java:   21  nrepl.SessionThread/run

also not using regular deps.edn and then restarting the REPL:

afrolabs/afrolabs-clj {:git/url ""
                                                     :git/sha "5222f9cade48491906744f5c924749f6c69b7803"}

You need this top-level in deps.edn :

:mvn/repos {"confluent" {:url ""}}

👍 1

How to "preplib" a library that is in an alias?

$ clj -X:deps:dev prep
Error building classpath. The following libs must be prepared before use: [afrolabs/afrolabs-clj]
borkdude@Mac ~/dev/clerk (main*) $ clj -X:dev:deps prep
Error building classpath. The following libs must be prepared before use: [afrolabs/afrolabs-clj]

lol - if you know then you may tell me too

lol, ok. I moved it temporarily to the main deps

What namespace is the -kafka/ alias from?

[afrolabs.components.kafka :as -kafka]
   [afrolabs.components.kafka.utilities :as -kafka-utils]

ok. I don't get an analysis failure with this repro:

(ns scratch
  (:require [afrolabs.components.kafka :as -kafka]
            [afrolabs.components.kafka.utilities :as -kafka-utils]
            [nextjournal.clerk :as clerk]))

(def cfg)
(def msgs-when-to-start)

^{::clerk/visibility {:code :show :result :hide}}
(defn dude []
  (let [msgs
        (let [_run-at #inst "2025-05-06T07:16:47.060791288Z"]
          (-kafka-utils/load-messages-from-confluent-topic :bootstrap-server (:kafka-bootstrap-server cfg)
                                                           :api-key           (:confluent-api-key cfg)
                                                           :api-secret        (:confluent-api-secret cfg)
                                                           :topics            ["v2-update-product"]
                                                           :extra-strategies  [(-kafka/StringSerializer :consumer :key)
                                                                               (-kafka/EdnSerializer :consumer :value)
                                                                               (-kafka/SeekToTimestampOffset msgs-when-to-start)
                                                                               ]
                                                           :collect-messages? true
                                                           :nr-msgs           1000))]
    (first msgs)))
Do you? (if you paste it in a scratch.clj file)

Locally, I'm still seeing LOTS of clojure.tools.analyzer.* in the stacktrace. I have to understand why that's happening first. 🤔

then you're not on the main branch of clerk

or you have some junk in "target" or so

👀 1

io.github.nextjournal/clerk {:git/sha "f4c5488e36c8df11fe352889544e7deb9af73cb7"}

I have this active alias:

:notebooks
           {:extra-paths ["notebooks"]
            :extra-deps {io.github.nextjournal/clerk {#_#_:mvn/version "0.17.1102"
                                                      :git/url "git@github.com:nextjournal/clerk.git"
                                                      :git/sha "f4c5488e36c8df11fe352889544e7deb9af73cb7"}
                         ;; :exclusions [org.clojure/tools.analyzer
                         ;;              org.clojure/tools.analyzer.jvm
                         ;;              borkdude/edamame
                         ;;              io.methvin/directory-watcher
                         ;;              org.clojure/core.memoize
                         ;;              org.ow2.asm/asm
                         ;;              org.clojure/core.cache
                         ;;              org.clojure/data.priority-map]
                         }}
Those exclusions used to be required in a previous iteration using project.clj.

and you don't accidentally have some other clerk somewhere else in the deps.edn?

Perhaps also try clj -X:notebooks:deps tree from the command line and paste the output here?

ok core.async still pulls in tools.analyzer . org.clojure/core.async 1.8.741 . org.clojure/tools.analyzer.jvm 1.3.2 . org.clojure/tools.analyzer 1.2.0 but it's no longer used by clerk

hey wait, clerk isn't even part of the output, what's going on?

I don't know.

Oh I might know, -X:deps requires you to supply aliases as an argument facepalm

clj -X:deps tree :aliases '[:notebooks]'

🌟 1

and it's still core.async that is pulling it in...

that's expected. but it's not expected that you would get a tools.analyzer stacktrace from clerk (unless you or one of your dependencies is using core.async in your notebook)

yes, definitely core.async in that -kafka-utilities/load-... fn in the notebook

feel free to still provide a repro though. not sure why it would work in the REPL but not in the notebook

👍🏽 1

Ah, regarding the preplib + aliases thing, same issue:

clj -X:deps prep :aliases '[....]'

🙏🏽 1

Hi @borkdude, thanks for your help yesterday. I made the repro repo here: https://github.com/pieterbreed/repro-clerk-analyzer , but in the repro it works fine! It's not crashing like with whatever setup I've got going in the main (work) project.

hmmm thanks for the repro though! did you try to turn your REPL off and on again? 😅

If some inspiration strikes and I know what to change on my work project, or how to force the regression on the repro-repo, then I'll ping you again, but I honestly think it's probably the former. Edit: Meaning it's probably a configuration issue on my side. I just have to find it.

It triggers repeatably in the work repository.

thanks, happy to be pinged again

🙏🏽 1