Working on a personal but open-source task management app for myself but mostly wanted to preview a finite-state-machine library I've been working on. It's intended to be framework agnostic with the ability to implement an IStateMachine protocol to create adapters for uix, replicant, and we'll see what else. For this app, I'm just using the basic atom-fsm but swapping out atoms for reagent-atoms. Below is a recording of using it to implement a fun slide to delete system as I realized I just don't want to implement another confirm modal system, so wanted to try something fun. Anyway, the state machine library is https://github.com/jaidetree/finity and the slide UI in the video is at https://github.com/jaidetree/tasky/blob/e063511423f80fea11493906d4a24a5e34db4db9/frontend/src/main/dev/jaide/tasky/views/delete_rocker.cljs#L66. Looking to release the FSM library sometime this month.
There's a spec->diagram function to take the fsm-spec atoms and transform them into mermaid charts. On this project, I've just been rendering them on the bottom of the page so they update when the ns reloads
interesting. wrote a fsm thing for myself recently. less formal though, but the graph thing is nice
minor nitpick but there is a [clojure.pprint :refer [pprint]] require in the core ns but never used
this is never DCE'd so just makes the build larger for no reason ๐
Cool!
Good catch! Fortunately it's still pre-release at this point. Is there a clever way to provide it during dev builds but in release builds replace it with (fn [_ & [_]] nil)?
of course there is an option for almost everything ๐
:release {:build-options {:ns-aliases {cljs.pprint your.fake-pprint}}}
(ns your.fake-pprint) (defn pprint [x]) or so
but its an option a user would have to provide in their build config, so not ideal in a library setting
What I've done for similar stuff is to have prod/mylib/myns.cljc and dev/mylib/myns.cljc that define alternatives for the same namespace, and include one in the production/release build and the other during development.
yeah, that works too. not a fan of having to switch the classpath so, since that requires a new JVM (kindof) ๐
It's not super ergonomic, but for ~static stuff you rarely need to fuzz with, it works well enough
Thanks! ns-aliases is exactly what I was looking for. I think what I'll do is inverse that, define a my-project.utils/pprint with a docstr that by default is (defn pprint [x]) then on dev builds alias it to cljs.pprint. That should protect me from myself in the future I think
Heh - fun. Have you any plans to implement hierarchical state support? I've been iterating on an in-house embedded C framework for years that's basically a publish-subscribe event broker where each subscriber is an hierarchical state machine (written in a handy DSL abusing variadic macros) - it's a nice way to work with in that context. Have been playing with cljs-statecharts in my cljs/replicant experiments to replicate (hoho) some of the benefits.
I just hit my first use-case for that where my tasks-fsm maps all the tasks from a fetch request to a task-fsm as part of its transition. That has been working well so far given I can leverage the fsm/subscribe function to make them talk to each other as needed. I am still thinking on how to better support that.
One idea that just came to mind this morning is to have some system level actions that are dispatched to the state machine and up to implementers and subscribers to handle. This way if you run (fsm/destroy a-task-fsm) it could broadcast a :fsm/destroy action to subscribers which could be used to remove it from the parent. That could cover most use-cases flexibly at the cost of some verbosity. Then on top of that I was thinking of adding :relationships to the fsm-spec so that you could express:
:relationships {:tasks {:has_many :tasks}}
which would inform the fsm that in the :tasks state the [:context :tasks] are children state machines which would take care of setting up the subscription for the :fsm/destroy system action and destroying the children when the parent is destroyed. Needs some more thought though.Strange. Just setup :dev {:build-options {:ns-aliases {dev.jaide.pprint cljs.pprint}}} and now the compile step hangs then aborts with the error:
[:test] Build failure:
Multiple files failed to compile.
aborted par-compile, [:shadow.build.classpath/resource "dev/jaide/valhalla/context_test.cljs"] still waiting for #{dev.jaide.valhalla.context}
{:aborted [:shadow.build.classpath/resource "dev/jaide/valhalla/context_test.cljs"], :pending #{dev.jaide.valhalla.context}}
ExceptionInfo: aborted par-compile, [:shadow.build.classpath/resource "dev/jaide/valhalla/context_test.cljs"] still waiting for #{dev.jaide.valhalla.context}
shadow.build.compiler/par-compile-one (compiler.clj:1232)
shadow.build.compiler/par-compile-one (compiler.clj:1197)
shadow.build.compiler/par-compile-cljs-sources/fn--15624/iter--15646--15650/fn--15651/fn--15652/fn--15653 (compiler.clj:1315)
clojure.core/apply (core.clj:667)
clojure.core/with-bindings* (core.clj:1990)
clojure.core/with-bindings* (core.clj:1990)
clojure.core/apply (core.clj:671)
clojure.core/bound-fn*/fn--5837 (core.clj:2020)
java.util.concurrent.FutureTask.run (FutureTask.java:317)
java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1144)
java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:642)
java.lang.Thread.run (Thread.java:1583)
aborted par-compile, [:shadow.build.classpath/resource "shadow/test/node.cljs"] still waiting for #{dev.jaide.valhalla.integration-test dev.jaide.valhalla.js-test dev.jaide.valhalla.core-test dev.jaide.valhalla.context
-test}
[:test] Build failure:
Multiple files failed to compile.
aborted par-compile, [:shadow.build.classpath/resource "dev/jaide/valhalla/context_test.cljs"] still waiting for #{dev.jaide.valhalla.context}
{:aborted [:shadow.build.classpath/resource "dev/jaide/valhalla/context_test.cljs"], :pending #{dev.jaide.valhalla.context}}
ExceptionInfo: aborted par-compile, [:shadow.build.classpath/resource "dev/jaide/valhalla/context_test.cljs"] still waiting for #{dev.jaide.valhalla.context}
shadow.build.compiler/par-compile-one (compiler.clj:1232)
shadow.build.compiler/par-compile-one (compiler.clj:1197)
shadow.build.compiler/par-compile-cljs-sources/fn--15624/iter--15646--15650/fn--15651/fn--15652/fn--15653 (compiler.clj:1315)
clojure.core/apply (core.clj:667)
clojure.core/with-bindings* (core.clj:1990)
clojure.core/with-bindings* (core.clj:1990)
clojure.core/apply (core.clj:671)
clojure.core/bound-fn*/fn--5837 (core.clj:2020)
java.util.concurrent.FutureTask.run (FutureTask.java:317)
java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1144)
java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:642)
java.lang.Thread.run (Thread.java:1583)
aborted par-compile, [:shadow.build.classpath/resource "shadow/test/node.cljs"] still waiting for #{dev.jaide.valhalla.integration-test dev.jaide.valhalla.js-test dev.jaide.valhalla.core-test dev.jaide.valhalla.context
-test}
odd, looks fine to me
maybe try wiping .shadow-cljs/builds and restarting with watch or compile. could be that :ns-aliases doesn't cause the cache to invalidate properly
Ran rm -rf .shadow-cljs/builds, restarted shadow-cljs watch test, unfortunately still hanging
{:deps {:aliases [cljs]}
:builds
{:test {:target :node-test
:output-to "build/js/node-tests.js"
:ns-regexp "-test"
:autorun true
:compiler-options {:warnings {:invalid-arithmetic false}}
:devtools {:preloads [cljs.pprint]}
:dev {:build-options {:ns-aliases {dev.jaide.pprint cljs.pprint}}}
:release {:autorun false
:compiler-options {:optimizations :simple
:elide-asserts false}}}}}:devtools {:preloads [cljs.pprint]} maybe try removing that? shouldn't hurt but otherwise no guesses
maybe push it somewhere so I can try locally ๐
Works for me
Miro Samek - author of a C/C++ UML statechart framework (QP) wrote a book I found very helpful years ago while writing my own. A good deal of the material should be implementation language agnostic, and see now the digital edition is free to download these days. He has a lot of lived experience working with this pattern. See here: https://www.state-machine.com/psicc2
Thanks @cormacc I'll take a look
hmm ok. I don't know how :ns-aliases have ever worked.
> aborted par-compile, [:shadow.build.classpath/resource "dev/jaide/valhalla/context.cljs"] still waiting for #{dev.jaide.pprint}
its basically waiting for the ns to be compiled that will never be compiled because its aliased
will fix
Thank you!
just pushed 3.0.3 to fix the par-compile issue. should be all good now
It works now! Thanks again
I have found an issue with Shadow-cljs 3.0.2. When I have it as a dev dependancy in package.json I get this issue:
shadow-cljs - config: /home/dev/src/mono/bec-core-client/shadow-cljs.edn
shadow-cljs - starting via "clojure"
shadow-cljs - HTTP server available at
shadow-cljs - server version: 3.0.2 running at
shadow-cljs - nREPL server started on port 4005
shadow-cljs - watching build :app
[:app] Configuring build.
[:app] Compiling ...
[:app] Build failure:
The required JS dependency "process" is not available, it was required by "node_modules/react/cjs/react.production.js".
Dependency Trace:
bec_core/cljs/core.cljs
bec_cljs_common/utils/interop.cljs
bec_cljs_common/utils/formatting.cljs
re_frame/core.cljc
re_frame/events.cljc
re_frame/db.cljc
re_frame/interop.cljs
reagent/core.cljs
node_modules/react/index.js
node_modules/react/cjs/react.production.js
Searched for npm packages in:
/home/dev/src/mono/bec-core-client/node_modules
See:
But if I downgrade it to 2.28.21 (haven't tested others) in package.json and use whatever version in deps.edn, then it works fine. Any ideas why?When I had version 3.0.2 in package.json and version 2.28.21 in deps.edn, the message I got was slightly different:
shadow-cljs - config: /home/dev/src/mono/bec-core-client/shadow-cljs.edn
shadow-cljs - starting via "clojure"
shadow-cljs - HTTP server available at
shadow-cljs - server version: 2.28.21 running at
shadow-cljs - nREPL server started on port 4005
shadow-cljs - watching build :app
[:app] Configuring build.
[:app] Compiling ...
[:app] Build failure:
The required JS dependency "process" is not available, it was required by "node_modules/react/cjs/react.production.js".
Dependency Trace:
bec_core/cljs/core.cljs
bec_cljs_common/utils/interop.cljs
bec_cljs_common/utils/formatting.cljs
re_frame/core.cljc
re_frame/events.cljc
re_frame/db.cljc
re_frame/interop.cljs
reagent/core.cljs
node_modules/react/index.js
node_modules/react/cjs/react.production.js
Searched for npm packages in:
/home/dev/src/mono/bec-core-client/node_modules
process is part of the node-libs-browser polyfill package to provide node-native package support
for none-node builds. You should install shadow-cljs in your project to provide that dependency.
npm install --save-dev shadow-cljs
See: I removed the automatic polyfilling of node built-in packages. process is one of them. you can just npm install process to fix this one
Alright. Sounds good. Thanks! ๐
or add npm install node-libs-browser to get the whole previous thing, but its been deprecated for like 5 years, so it time to get rid of that ๐
maybe I'll fix this internally though. forgot about react doing this nonsense. this isn't intentional ๐
I have now added the process package and it fixed my issue with react. So if you add it internally then just mention it somewhere so I can remove it from package.json again ๐
This is also required for plotly.js (and supabase libs require buffer) -- both readily resolved via explicit install as suggested.
We have had a similar issue to @shakof91 mentioned https://clojurians.slack.com/archives/C6N245JGG/p1745575597950839 with version https://clojurians.slack.com/archives/C6N245JGG/p1745479994211049.
However, ours comes from a dependency on a library qr-image which claims to have zero dependencies but in fact requires both stream and zlib and while running npm install stream resolved the stream dependency, when running npm install zlib we get the following error:
npm error code 127
npm error path /home/stuart/work/portal/node_modules/zlib
npm error command failed
npm error command sh -c node-waf clean || true; node-waf configure build
npm error sh: 1: node-waf: not found
npm error sh: 1: node-waf: not found
npm error A complete log of this run can be found in: ~/.npm/_logs/2025-04-25T11_04_32_299Z-debug-0.log
Any thoughts?those are also node built-ins. previously it used stream via stream-browserify and zlib via browserify-zlib. install those and set
:js-options
{:resolve
{"zlib" {:target :npm :require "browserify-zlib"}
"stream" {:target :npm :require "stream-browserify"}}}
in your build configof course also need to manually install those packages via npm install
I guess I should actually test how webpack handles this these days, could have sworn it just fails as well
well, package is 8 years old. maybe not many people actually using this in the browser?
ERROR in ./node_modules/qr-image/lib/qr.js 3:15-41
Module not found: Error: Can't resolve 'stream' in '/Users/thheller/code/tmp/webpack-test/node_modules/qr-image/lib'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "stream": require.resolve("stream-browserify") }'
- install 'stream-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "stream": false }
resolve 'stream' in '/Users/thheller/code/tmp/webpack-test/node_modules/qr-image/lib'
Parsed request is a moduleok actually fails as expected
maybe should add a more detailed error message like that, otherwise I'll probably be answering questions like that for a while ๐
false also works, but I'm assuming the packages are actually used so that would probably break something
as in {"zlib" false}
Thank you, that has worked following require a few more dependencies, namely events , process &`assert` .
yikes, you are on some really old packages I guess? ๐
most popular stuff has been updated by now
The qr-image is quite old, we may look for a newer equivalent, but we liked the fact it had "zero" dependencies ๐.
I think a more detailed error message similar to webpack would have helped, or at a minimum in the docs have an example of the indirection required for the node built-ins in the https://shadow-cljs.github.io/docs/UsersGuide.html#_missing_js_dependency (or maybe just a mention in the changelog).
Thank you again for helping us out
I'm trying (in admittedly rather stupid ways) to switch a large project from leiningen + figwheel-main to deps.edn, tools.build and shadow-cljs. As I'm banging my head against various obstacles, I encountered this:
[2025-04-25 20:28:57.370 - WARNING] :shadow.cljs.devtools.errors/format-error
Note: The following stack trace applies to the reader or compiler, your code was not executed.
ExceptionInfo #:clojure.error{:source nil, :line 689, :column 14, :phase :compilation}
cljs.analyzer/macroexpand-1 (analyzer.cljc:4114)
cljs.analyzer/macroexpand-1 (analyzer.cljc:4110)
cljs.analyzer/analyze-seq (analyzer.cljc:4147)
cljs.analyzer/analyze-seq (analyzer.cljc:4127)
cljs.analyzer/analyze-form (analyzer.cljc:4336)
cljs.analyzer/analyze-form (analyzer.cljc:4333)
cljs.analyzer/analyze* (analyzer.cljc:4389)
cljs.analyzer/analyze* (analyzer.cljc:4381)
cljs.analyzer/analyze (analyzer.cljc:4409)
cljs.analyzer/analyze (analyzer.cljc:4392)
cljs.analyzer/analyze (analyzer.cljc:4402)
cljs.analyzer/analyze (analyzer.cljc:4392)
Caused by:
ExceptionInfo Cannot invoke "java.util.concurrent.Future.get()" because "fut" is null at line 689 {:file nil, :line 689, :column 14, :tag :cljs/analysis-error}
cljs.analyzer/error (analyzer.cljc:787)
cljs.analyzer/error (analyzer.cljc:783)
cljs.analyzer/macroexpand-1 (analyzer.cljc:4114)
cljs.analyzer/macroexpand-1 (analyzer.cljc:4110)
Caused by:
NullPointerException Cannot invoke "java.util.concurrent.Future.get()" because "fut" is null
clojure.core/deref-future (core.clj:2317)
clojure.core/deref (core.clj:2337)
clojure.core/deref (core.clj:2323)
cljs.analyzer/excluded? (analyzer.cljc:3953)
nil
That message isโฆ not very helpful ๐ so I looked at shadow-cljs sources to find where this :shadow.cljs.devtools.errors/format-error appears and it appears in a function called user-friendly-error ๐
Any pointers/hints? This error appears after lots of "Compile CLJS" and "Cache write" messages when I run npx shadow-cljs -v release appI suspect this is from a dependency conflict? using a bad mix of clojurescript/shadow-cljs. make sure you have the clojurescript version appropriate for the shadow-cljs version you are using
check which via https://clojars.org/thheller/shadow-cljs
"3.0.2" -> cljs 1.12.35
org.clojure/clojurescript {:mvn/version "1.12.35"} is what I have in my deps.edn.
and which shadow-cljs version?
3.0.2.
No, hold on. I'm new to this package.json thing, and it says 2.28.2. I'll check again.
that doesn't matter if you have deps.edn
package.json only controls the npm package version, which isn't relevant here if deps.edn is actually used. as in :deps .. in shadow-cljs.edn
in which context does this error appear btw? during a build or during load or something?
@env/*compiler* this fails, but that binding should most definitely be set. otherwise nothing would work at all
so must be something rather drastic missing?
Ok, I got it to say 3.0.2 in all package*json files as well. But I still get the same error message. I get it when running npx shadow-cljs -v release app so just trying to build the app. And I'm sorry for newbie questions, but I managed to develop and maintain a large Clojure+ClojureScript app for 10 years now without even touching npm, so I don't understand its workings yet.
My configuration is most definitely broken at this point, I'm just looking for pointers on where to look.
Well, something must be working, because that message is after compiling hundreds of ClojureScript files. At least that's what -v says.
well its an internal error, so something rather funky must be going on ๐
could be a macro doing something nasty? need more context to give more useful comments
is it failing in a particular stage or file?
Well, the last message from -v are:
<- Compile CLJS: pbx/scan_ui.cljs (92 ms)
-> Cache write: pbx/scan_ui.cljs
<- Cache write: pbx/projects/cards.cljs (48 ms)
<- Cache write: pbx/projects/import_ui.cljs (42 ms)
<- Cache write: pbx/scan_ui.cljs (28 ms)
[2025-04-25 20:43:44.315 - WARNING] :shadow.cljs.devtools.errors/format-error
And it manages to go through hundreds of files before thatโฆ I definitely do have some weird macrology, but nothing specifically in these files. These are actually rather boring.its weird that this happens in macroexpand. a stage where this binding will most definitely be set
maybe we can do with a better stacktrace (as in not user friendlified ๐
npx shadow-cljs clj-repl then (shadow/release! :app)
actually I have a guess. a macro throwing something containing a lazy-seq and that seq getting realized as part of trying to print it
might just be hiding the actual error in some way ๐
shadow.user=> (shadow/release! :app)
[:app] Compiling ...
Unexpected error (NullPointerException) compiling at (REPL:689:14).
Cannot invoke "java.util.concurrent.Future.get()" because "fut" is null
shadow.user=>
๐gotta hate stack traces just being ommitted ๐
try with :jvm-opts ["-XX:-OmitStackTraceInFastThrow"] in your deps.edn added
hmm maybe that is only allowed in aliases? can't remember ๐
clj -J-XX:-OmitStackTraceInFastThrow then (require '[shadow.cljs.devtools.api :as shadow]) and (shadow/release! :app)
it was -J right? can't remember exactly
Hmm. I can't seem to add :jvm-opts in deps.edn so that they are actually used. I'm adding an invalid option to check. Toplevel :jvm-opts seem to be ignored.
clj -J-XX:-OmitStackTraceInFastThrow seems to work, so just try that
And as for aliases, I'm not sure who or what decides which alias is used if I run 'npx shadow-cljs -v release app'
:deps {:aliases [:foo]} decides, true is just short for no aliases
Thanks! Trying it now.
but just go with the clj command, skips all the npm stuff.
Ok. I got it to use the option, but it doesn't produce any more output than before. Same with the clj command.
hmm might be the repl just dropping it
just look at it with *e maybe? as in type that in the repl after the error occurred?
just need the full trace somehow ๐
I swear I have regretted shortening stack traces every single damn time
or lets go really deep
in the REPL before running (shadow/release! :app) instead do (in-ns 'shadow.cljs.devtools.errors) and then
(defn user-friendly-error [e]
(binding [*out* *err*]
(try
(println (error-format e))
(catch Throwable ex
(prn e)
(log/warn-ex ex ::format-error)
(println (.getMessage ex)))))
:error)then (in-ns 'shadow.user) (shadow/release :app) not release! so the fn is actually called
the actual error trying to be printed might be more useful anyway
Ok, now we're getting somewhere (*e).
#error {
:cause "Cannot invoke \"java.util.concurrent.Future.get()\" because \"fut\" is null"
:via
[{:type clojure.lang.ExceptionInfo
:message nil
:data #:clojure.error{:source nil, :line 689, :column 14, :phase :compilation}
:at [cljs.analyzer$macroexpand_1 invokeStatic "analyzer.cljc" 4114]}
{:type clojure.lang.ExceptionInfo
:message "Cannot invoke \"java.util.concurrent.Future.get()\" because \"fut\" is null at line 689 "
:data {:file nil, :line 689, :column 14, :tag :cljs/analysis-error}
:at [cljs.analyzer$error invokeStatic "analyzer.cljc" 787]}
{:type java.lang.NullPointerException
:message "Cannot invoke \"java.util.concurrent.Future.get()\" because \"fut\" is null"
:at [clojure.core$deref_future invokeStatic "core.clj" 2317]}]
:trace
[[clojure.core$deref_future invokeStatic "core.clj" 2317]
[clojure.core$deref invokeStatic "core.clj" 2337]
[clojure.core$deref invoke "core.clj" 2323]
[cljs.analyzer$excluded_QMARK_ invokeStatic "analyzer.cljc" 3953]
[cljs.analyzer$excluded_QMARK_ invoke "analyzer.cljc" 3949]
[cljs.analyzer$get_expander_STAR_ invokeStatic "analyzer.cljc" 3982]
[cljs.analyzer$get_expander_STAR_ invoke "analyzer.cljc" 3980]
[cljs.analyzer$get_expander invokeStatic "analyzer.cljc" 4009]
[cljs.analyzer$get_expander invoke "analyzer.cljc" 4005]
[cljs.analyzer$macroexpand_1_STAR_ invokeStatic "analyzer.cljc" 4058]
[cljs.analyzer$macroexpand_1_STAR_ invoke "analyzer.cljc" 4048]
[cljs.analyzer$macroexpand_1 invokeStatic "analyzer.cljc" 4114]
[cljs.analyzer$macroexpand_1 invoke "analyzer.cljc" 4110]
[cljs.analyzer$analyze_seq invokeStatic "analyzer.cljc" 4147]
[cljs.analyzer$analyze_seq invoke "analyzer.cljc" 4127]
[cljs.analyzer$analyze_form invokeStatic "analyzer.cljc" 4336]
[cljs.analyzer$analyze_form invoke "analyzer.cljc" 4333]
[cljs.analyzer$analyze_STAR_ invokeStatic "analyzer.cljc" 4389]
[cljs.analyzer$analyze_STAR_ invoke "analyzer.cljc" 4381]
[cljs.analyzer$analyze invokeStatic "analyzer.cljc" 4409]
[cljs.analyzer$analyze invoke "analyzer.cljc" 4392]
[cljs.analyzer$analyze invokeStatic "analyzer.cljc" 4402]
[cljs.analyzer$analyze invoke "analyzer.cljc" 4392]
[cljs.analyzer$analyze invokeStatic "analyzer.cljc" 4400]
[cljs.analyzer$analyze invoke "analyzer.cljc" 4392]
[clojure.lang.Var invoke "Var.java" 390]
[daiquiri.compiler$infer_tag$fn__25218 invoke "compiler.clj" 53]
[clojure.lang.AFn applyToHelper "AFn.java" 152]
[clojure.lang.AFn applyTo "AFn.java" 144]
[clojure.core$apply invokeStatic "core.clj" 667]
[clojure.core$with_bindings_STAR_ invokeStatic "core.clj" 1990]
[clojure.core$with_bindings_STAR_ doInvoke "core.clj" 1990]
[clojure.lang.RestFn invoke "RestFn.java" 428]
[daiquiri.compiler$infer_tag invokeStatic "compiler.clj" 51]
[daiquiri.compiler$infer_tag invoke "compiler.clj" 44]
[daiquiri.compiler$element_compile_strategy invokeStatic "compiler.clj" 310]
[daiquiri.compiler$element_compile_strategy invoke "compiler.clj" 288]
[clojure.lang.MultiFn invoke "MultiFn.java" 233]
[daiquiri.compiler$compile_html invokeStatic "compiler.clj" 403]
[daiquiri.compiler$compile_html invoke "compiler.clj" 397]
[daiquiri.compiler$eval25282$fn__25284$fn__25289 invoke "compiler.clj" 183]
[clojure.core$map$fn__5954 invoke "core.clj" 2772]
[clojure.lang.LazySeq force "LazySeq.java" 50]
[clojure.lang.LazySeq realize "LazySeq.java" 89]
it's longer, but I think this is the interesting part. Daiquiri is a library used by Rum to process hiccup.more, gimme more ๐ that is still realizing a lazy seq
I'm trying to figure out how to get you the full stacktrace. Seemingly simple thing, butโฆ
I know there are some other rum users and never had a report like this
(prn *e) maybe?
Ok, got it.
maybe just try upgrading rum ๐
Just to clarify: this is from an actively maintained app which compiles and runs just fine (leiningen, figwheel-main). It uses the latest libraries whenever it makes sense.
I do intend to move from Rum to UIx, but I can't do everything at once, and just this switch of build systems will likely take me a week or two to sort out. It's a big app (150kLOC, nearly 600 source files).
I have to go now, but I'll be back first thing tomorrow morning. If you come up with any pointers for me to try, I'd be very grateful, and if not, I'll just keep trying I guess. Many, many thanks for your help! ๐
I see no reason why rum wouldn't work
what I don't get is how shadow is nowhere in this trace, almost like this is starting a new thread or something which of course would not have that binding
from that trace I'd say you are not using shadow at all ๐ not even a single line. shadow-cljs should be in there for sure
so my guess is that this is a secondary exception that happens while trying to print the first
otherwise I can't explain why the REPL is in this trace but shadow-cljs is not
try (try (shadow/release! :app) (catch Exception e (tap> e)) then look at in inspect via http://localhost:9630/inspect-latest
that'll need shadow-cljs running though, so ((requiring-resolve 'shadow.cljs.devtools.server/start!)) first
I'm having trouble getting something. I did this, but it just hangs forever and there is nothing in the inspector:
clj -J-XX:-OmitStackTraceInFastThrow ๎ฒ โ ๎ณ 1m 46s
Clojure 1.12.0
user=> (require '[shadow.cljs.devtools.api :as shadow])
nil
user=> ((requiring-resolve 'shadow.cljs.devtools.server/start!))
Apr 26, 2025 9:27:18 AM io.undertow.Undertow start
INFO: starting server: Undertow - 2.3.10.Final
Apr 26, 2025 9:27:18 AM org.xnio.Xnio <clinit>
INFO: XNIO version 3.8.8.Final
Apr 26, 2025 9:27:18 AM org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.8.8.Final
Apr 26, 2025 9:27:18 AM org.jboss.threads.Version <clinit>
INFO: JBoss Threads version 3.5.0.Final
Apr 26, 2025 9:27:19 AM io.undertow.Undertow start
INFO: starting server: Undertow - 2.3.10.Final
shadow-cljs - HTTP server available at
shadow-cljs - server version: 3.0.2 running at
shadow-cljs - nREPL server started on port 7888
:shadow.cljs.devtools.server/started
user=> (try (shadow/release! :app) (catch Exception e (tap> e))
But the exception is happening, if I do the same thing without the catch:
clj -J-XX:-OmitStackTraceInFastThrow ๎ฒ โ INT ๎ณ 1m 42s
Clojure 1.12.0
user=> (require '[shadow.cljs.devtools.api :as shadow])
nil
user=> ((requiring-resolve 'shadow.cljs.devtools.server/start!))
Apr 26, 2025 9:28:57 AM io.undertow.Undertow start
INFO: starting server: Undertow - 2.3.10.Final
Apr 26, 2025 9:28:57 AM org.xnio.Xnio <clinit>
INFO: XNIO version 3.8.8.Final
Apr 26, 2025 9:28:57 AM org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.8.8.Final
Apr 26, 2025 9:28:57 AM org.jboss.threads.Version <clinit>
INFO: JBoss Threads version 3.5.0.Final
Apr 26, 2025 9:28:58 AM io.undertow.Undertow start
INFO: starting server: Undertow - 2.3.10.Final
shadow-cljs - HTTP server available at
shadow-cljs - server version: 3.0.2 running at
shadow-cljs - nREPL server started on port 7888
:shadow.cljs.devtools.server/started
user=> (shadow/release! :app)
[:app] Compiling ...
Unexpected error (NullPointerException) compiling at (REPL:689:14).
Cannot invoke "java.util.concurrent.Future.get()" because "fut" is null
user=> Hmm. Is the REPL in the dev tools supposed to work? It seems completely broken for me.
define broken? for some reason ctrl+enter doesn't work on my mac, as in the enter above the shift key
ctrl+enter on they keypad works just fine
but yeah the whole thing is more of a work in progress. didn't quite finish it the last time I worked on it
another thing you could try is go closer to the actual failing point
so open the REPL as usual
(require 'daiquiri.compiler)
(defonce orig daiquiri.compiler/infer-tag)
(alter-var-root #'daiquiri.compiler/infer-tag
(fn [_]
(fn [env form]
(try
(orig env form)
(catch Exception e
(tap> [:infer-ex form env e])
(throw (ex-info "failed with form" {:form form} e))
)))))before or after ((requiring-resolve 'shadow.cljs.devtools.server/start!))
then (require '[shadow.cljs.devtools.api :as shadow]) and (shadow/release! :app)
although I still kinda expect this to fail at the "wrong" stage. I still can't explain how this would ever be nil or why shadow isn't anywere in the stacktrace
do you trigger some custom compilation via macros or something?
oh and please try :compiler-options {:parallel-build false}. it is true by default, but maybe it doesn't like being in multiple threads
Well, I wrote "broken", because evaluating (+ 2 2) in that REPL produces "Cannot invoke \"clojure.lang.Namespace.getMapping(clojure.lang.Symbol)\" because the return value of \"clojure.lang.Compiler.currentNS()\" is null". All I can evaluate are single values.
there is something really weird going on in your project ๐
No, it's me. I break software. I'm not even kidding. Things generally break around me, so much that my family sometimes tells me to step away 10m or so if an ATM machine fails.
But, we're getting somewhere! I do have something in the inspector!
do you have a user.clj in your project? do you use tools.namespace and anything that generally messes with namespaces/classpath or any of that stuff
No, nothing like that. But here is what the inspector shows.
There is nothing particularly weird about that place in the code, apart from the fact that that function is BIG.
Also, it is quite old, hasn't been changed lately, and compiles just fine with figwheel-main.
did you try :compiler-options {:parallel-build false}? I don't believe figwheel has that enabled by default
if you click the #error line in the inspect
I assume that shows the same stack as yesterday? without any shadow-cljs?
My app normally builds ClojureScript 20 times (once for each language) and 19 of those are always parallel and one always isn't. So both options were exercised. Right now I have :compiler-options {:parallel-build false} in shadow-cljs.edn.
Yes. It's the same thing.
:parallel-build is only affecting the namespaces being compiled for a singular build, it never affects multiple builds in any way, as in they won't be parallel or so
I understand, my point was that both versions were tried every time I built a release version.
e.g. 19 builds were run with parallel-build set to true and one with parallel-built set to false.
I'd like to focus on getting one to compile, ignore the other 19 for now
Gladly. Should I try breaking up the function into smaller pieces?
no, there is absolutely no reason for this to not compile if figwheel is fine with it
and yetโฆ hold on, I think I found something by trying to compile the function with nobody. But I can't believe it. The function does destructuring and one of the parameters is destructured as:
{:project-id :project/id
project-build-quantities :project/build-quantities
sub-assembly-part-id :project/part-id
:as project}
See the problem?Removing the first colon makes it compile. But why did it ever work? ๐
and more mysterious is how does it and up with an exception trace like above?
do you ever use future or some other way of launching threads in macros? I still do not get how shadow-cljs isn't anywhere in the traces
Ok, so this took a while but I found it. That colon appeared thereโฆ Tue Jan 5 12:39:14 2021 +0100.
No, my macros do not do any work with future or launch threads. Howeverโฆ the ones for translations do use the nippy library.
I honestly have no idea how that code compiled, much less worked, for the last 4.5 years.
so the error is entirely gone now?
Yes. I now get through to "[:app] Build completed. (609 files, 52 compiled, 17 warnings, 14.69s)" and three bazillion warnings.
you mean 17?
31 | (defn whatever [{:kw-here :and-here}]
-------^------------------------------------------------------------------------
Syntax error macroexpanding cljs.core/defn.
Call to cljs.core/defn did not conform to spec.
-- Spec failed --------------------
(... [{:kw-here :and-here}] ...)
^^^^^^^^^^^^^^^^^^^^
has extra input
or
should satisfy
vector?ah wait daiquiri. let me try that
It looks like three bazillion, because they are multi-line, have colors, and are scary ๐ But yes.
The error that you've shown above is what I saw after I disabled the entire body of the function and replaced it with a single (do).
Re-enabling the (big) body caused the bizarre behavior we were investigating.
So, to summarize: my immediate problem is solved. I don't know if the bizarre behavior is something worth debugging.
I'd be super curious about a reproducible example, but without that probably not