Luminus: java.lang.IllegalArgumentException: Key must be integer

Ah I see. So here’s what’s happening in that case:

You specified no return, which per the documentation in HugSQL I think implies that the :raw result is the default if nothing is specified. In other words, there could be a value returned (as opposed to nil). Doing what you suggest (removing the return value specification) gets us a different error (in the same place though, the flash middleware).

ERROR rental.middleware - Key must be integer 
java.lang.IllegalArgumentException: Key must be integer
	at clojure.lang.APersistentVector.invoke(APersistentVector.java:294)
	at ring.middleware.flash$flash_response.invokeStatic(flash.clj:24)
	at ring.middleware.flash$flash_response.invoke(flash.clj:14)
	at ring.middleware.flash$wrap_flash$fn__8069.invoke(flash.clj:39)
	at ring.adapter.undertow.middleware.session$wrap_undertow_session$fn__8612.invoke(session.clj:77)
	at ring.middleware.keyword_params$wrap_keyword_params$fn__8706.invoke(keyword_params.clj:53)

If we alter the function we can see what the return is:

(fn [req]
    (let [res  (db/addb! {:name "jonas" :org "sdf" :parea 23 :barea 34 :tarea 65 :hyra 3232
                          :omk 4343 :tid 23 :start 43 :stop 56 :notes "notes" :kost 65 :kost2 34})
          _ (println res)]
      res))

Which prints (before the error):

[{:next.jdbc/update-count 1}]

So it looks like the default return is a vector with a map indicating the operation from next.jdbc. I am unfamiliar with this format and convention, but that’s not important. What is important is that we are getting a return value, and that it’s a clojure vector. So - as before - this value is passed along to the middleware when computing the response (just as the prior integer return value was), which ends up getting caught in the flash middleware:

at ring.middleware.flash$flash_response.invokeStatic(flash.clj:24)

If we jump to that line of code in the source, we see a line inside of a let:

(let [...
       session (if-let [flash (response :flash)] ...]
...
 )

So in this lexical scope, response is bound to the vector we produced. It is being applied as a function (which clojure data structures can be and is idiomatic). Since we are supposed to have a response map here, the data structure has a function implementation that equates to something like clojure.core/get (e.g. lookup the key associated with the value :flash). There is a wrinkle though, since we passed in a vector and not a map. Vectors have similar function behavior, in that they are presented as an implicit associative mapping of integer keys (indices) to values. So they have a map-like view and can be used as functions (assuming the input is an integer key). In this case, the input is a keyword…so this violates the contract for looking up keys in a vector, akin to the following:

user> (def response [{:next.jdbc/update-count 1}])
#'user/res
user> (response 0)
#:next.jdbc{:update-count 1}
user> (response :flash)
Execution error (IllegalArgumentException) at user/eval31008 (form-init6793830745011336222.clj:361).
Key must be integer

So if we again return a response map (or something that comports to the expectations of downstream middleware, aka anything that supports an associative map lookup - including nil) then we are okay again. I think the convention (if not requirement) is to always pass a map around.

2 Likes

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.