A Dependency Hell issue in Clojure
The story
We have a Clojure application that uses Jetty and Ring to serve itself as a web service. Recently, I'm thinking about adding a Swagger UI (or OpenAPI) to it, without having to manage a lot of Swagger assets by ourselves. luckily since we are using reitit
as the server router, it provides some built-in support for Swagger, even though it only supports up to Swagger 2.0. Everything is smooth until suddenly we hit a snag when trying to make it work:
- Following the instructions here: reitit docs for adding Swagger
- When try to
boot build
, some errors popped up:
...
java.lang.ClassNotFoundException: com.fasterxml.jackson.core.exc.InputCoercionException
java.lang.NoClassDefFoundError: com/fasterxml/jackson/core/exc/InputCoercionException
clojure.lang.ExceptionInfo: com/fasterxml/jackson/core/exc/InputCoercionException
- There is a few useful related information for debugging:
- By running
lein deps :tree
and looking at the results, it turns out thevault-clj
library is usingcheshire
, which specifies a different version ofcom.fasterxml.jackson.core/jackson-core
as its dependency fromreitit
's. Here is the related parts of the output:
[amperity/vault-clj "0.5.1"]
[amperity/envoy "0.3.1"]
[environ "1.1.0"]
[table "0.5.0"]
[cheshire "5.7.1"]
[com.fasterxml.jackson.core/jackson-core "2.8.6"]
[com.fasterxml.jackson.dataformat/jackson-dataformat-cbor "2.8.6"]
[com.fasterxml.jackson.dataformat/jackson-dataformat-smile "2.8.6"]
[tigris "0.1.1"]
[com.stuartsierra/component "0.3.2"]
[com.stuartsierra/dependency "0.2.0"]
[org.clojure/tools.logging "0.3.1"]
[metosin/reitit-swagger-ui "0.3.10"]
[metosin/jsonista "0.2.5"]
[com.fasterxml.jackson.core/jackson-databind "2.10.0"]
[com.fasterxml.jackson.core/jackson-annotations "2.10.0"]
[com.fasterxml.jackson.datatype/jackson-datatype-jsr310 "2.10.0"]
[metosin/ring-swagger-ui "2.2.10"]
- Looking at cheshire's issue, it seems even the latest version of
cheshire
does not point tojackson.core "2.10.0"
yet. - It is awkward, but for now the easiest solution for me is to force specify the version of
jackson.core
in myboot.build
file, part of which now looks like this:
(set-env!
:dependencies
'[[com.fasterxml.jackson.core/jackson-core "2.10.0"]
[amperity/vault-clj "0.5.1"]
[metosin/reitit "0.3.10"]
[metosin/reitit-swagger "0.3.10"]
[metosin/reitit-swagger-ui "0.3.10"]])
Apparently, most of the people who have run into this "Jackson" error ended up listing it explicitly like what I did... Check here for more discussions about this on Clojureverse
One more thing
Different languages/communities have different ways to manage the dependecies, e.g. JavaScript has the package-lock.json
, Python is still figuring out (there is a great potential tool I really liked: poetry, but it's still not yet widely adopted). Someone has proved that the dependency hell is a NP-Complete problem. While people are actively trying to improve on this problem, we should really stick with a minimum set of dependencies and avoid using unnecessary tools in our projects, i.e. don't re-invent wheels, but always use the indispensable wheels.
- Cover photo credit to Unsplash