weavejester / integrant

Micro-framework for data-driven architecture
MIT License
1.24k stars 64 forks source link

Clearer error messaging for undefined keys #111

Open enspritz opened 5 months ago

enspritz commented 5 months ago

I was adding in some new components into an Integrant configuration, and took the opportunity to see how Integrant reacts to keys being specified in the conf file but not implemented. Given

{:admin/jetty {}
 :admin/routes {}
 ... }

IG failed with:

Execution error (IllegalArgumentException) at integrant.core/eval9575$fn (core.cljc:404).
No such namespace: admin

I think a better error message here would speak directly to the cause, for example:

Reference/key is unknown to Integrant: :admin/jetty

Like Figwheel, it might even go on to suggest clues to assist in remediation, such as the fact that the key wasn't registered with IG using defmethod, no admin namespace was found, no symbol :admin/jetty was found in a present admin namespace, and so on..

weavejester commented 5 months ago

That looks like a regression. When you say "IG failed" can you give me the function that failed? Or a more complete stack trace? i.e. were you trying to init the config, or did it fail on expand or something like that?

enspritz commented 5 months ago

A reproduction of the error message and stack trace, but for a specifically bad integrant EDN configuration entry named :blargh, defined as follows in the file integrant-configuration.edn:

...
 :blargh {}
...

On the off chance it's germane to this issue, aero is being used to pre-process that EDN file.

Barf from that java process:

Execution error (AssertionError) at integrant.core/normalize-key (core.cljc:40).
Assert failed: (valid-config-key? key)

Full report at:
/tmp/clojure-17129211614840298835.edn

And that report file:

cat /tmp/clojure-17129211614840298835.edn
{:clojure.main/message
 "Execution error (AssertionError) at integrant.core/normalize-key (core.cljc:40).\nAssert failed: (valid-config-key? key)\n",
 :clojure.main/triage
 {:clojure.error/class java.lang.AssertionError,
  :clojure.error/line 40,
  :clojure.error/cause "Assert failed: (valid-config-key? key)",
  :clojure.error/symbol integrant.core/normalize-key,
  :clojure.error/source "core.cljc",
  :clojure.error/phase :execution},
 :clojure.main/trace
 {:via
  [{:type clojure.lang.Compiler$CompilerException,
    :message
    "Syntax error macroexpanding at (/tmp/form-init7291689155971165176.clj:1:73).",
    :data
    {:clojure.error/phase :execution,
     :clojure.error/line 1,
     :clojure.error/column 73,
     :clojure.error/source "/tmp/form-init7291689155971165176.clj"},
    :at [clojure.lang.Compiler load "Compiler.java" 7665]}
   {:type java.lang.AssertionError,
    :message "Assert failed: (valid-config-key? key)",
    :at [integrant.core$normalize_key invokeStatic "core.cljc" 40]}],
  :trace
  [[integrant.core$normalize_key invokeStatic "core.cljc" 40]
   [integrant.core$normalize_key invoke "core.cljc" 40]
   [integrant.core$eval8441$fn__8442 invoke "core.cljc" 383]
   [clojure.lang.MultiFn invoke "MultiFn.java" 233]
   [integrant.core$prep$fn__8514 invoke "core.cljc" 478]
   [clojure.lang.PersistentHashMap$NodeSeq
    kvreduce
    "PersistentHashMap.java"
    1307]
   [clojure.lang.PersistentHashMap$BitmapIndexedNode
    kvreduce
    "PersistentHashMap.java"
    802]
   [clojure.lang.PersistentHashMap
    kvreduce
    "PersistentHashMap.java"
    236]
   [clojure.core$fn__8525 invokeStatic "core.clj" 6909]
   [clojure.core$fn__8525 invoke "core.clj" 6889]
   [clojure.core.protocols$fn__8257$G__8252__8266
    invoke
    "protocols.clj"
    175]
   [clojure.core$reduce_kv invokeStatic "core.clj" 6920]
   [clojure.core$reduce_kv invoke "core.clj" 6911]
   [integrant.core$prep invokeStatic "core.cljc" 478]
   [integrant.core$prep invoke "core.cljc" 466]
   [integrant.core$prep invokeStatic "core.cljc" 474]
   [integrant.core$prep invoke "core.cljc" 466]
   [com.acme.server.core$start$fn__60504 invoke "core.clj" 164]
   [integrant.repl$prep$fn__10140 invoke "repl.clj" 16]
   [clojure.lang.AFn applyToHelper "AFn.java" 154]
   [clojure.lang.AFn applyTo "AFn.java" 144]
   [clojure.lang.Var alterRoot "Var.java" 308]
   [clojure.core$alter_var_root invokeStatic "core.clj" 5536]
   [clojure.core$alter_var_root doInvoke "core.clj" 5531]
   [clojure.lang.RestFn invoke "RestFn.java" 425]
   [integrant.repl$prep invokeStatic "repl.clj" 16]
   [integrant.repl$prep invoke "repl.clj" 14]
   [integrant.repl$go invokeStatic "repl.clj" 60]
   [integrant.repl$go invoke "repl.clj" 57]
   [integrant.repl$go invokeStatic "repl.clj" 58]
   [integrant.repl$go invoke "repl.clj" 57]
   [com.acme.server.core$start invokeStatic "core.clj" 165]
[ELIDED]
   [clojure.lang.RestFn applyTo "RestFn.java" 137]
   [clojure.lang.Var applyTo "Var.java" 705]
   [clojure.main main "main.java" 40]],
  :cause "Assert failed: (valid-config-key? key)",
  :phase :execution}}
weavejester commented 5 months ago

Thanks for the stacktrace, but that looks like a different error. Do you have a stacktrace for the original error?

enspritz commented 5 months ago

I apologize, yes, the key needs to be namespaced to reproduce the original error. Here it is:

File integrant-configuration.edn:

 :admin/blargh {}

Console output:

Execution error (IllegalArgumentException) at integrant.core/eval8467$fn (core.cljc:404).
No such namespace: admin

Full report at:
/tmp/clojure-18444096903419644454.edn

Snippet from report file:

{:clojure.main/message
 "Execution error (IllegalArgumentException) at integrant.core/eval8467$fn (core.cljc:404).\nNo such namespace: admin\n",
 :clojure.main/triage
 {:clojure.error/class java.lang.IllegalArgumentException,
  :clojure.error/line 404,
  :clojure.error/cause "No such namespace: admin",
  :clojure.error/symbol integrant.core/eval8467$fn,
  :clojure.error/source "core.cljc",
  :clojure.error/phase :execution},
 :clojure.main/trace
 {:via
  [{:type clojure.lang.Compiler$CompilerException,
    :message
    "Syntax error macroexpanding at (/tmp/form-init14155514920343356044.clj:1:74).",
    :data
    {:clojure.error/phase :execution,
     :clojure.error/line 1,
     :clojure.error/column 74,
     :clojure.error/source "/tmp/form-init14155514920343356044.clj"},
    :at [clojure.lang.Compiler load "Compiler.java" 7665]}
   {:type clojure.lang.ExceptionInfo,
    :message "Error on key :admin/blargh when building system",
    :data
    {:reason :integrant.core/build-threw-exception,
     :system
     { [ELIDED, ALTHOUGH NOTE THAT ERRANT ENTRY :admin/blargh DOESN'T APPEAR] },
    :at [integrant.core$build_exception invokeStatic "core.cljc" 324]}
   {:type java.lang.IllegalArgumentException,
    :message "No such namespace: admin",
    :at [clojure.lang.Var find "Var.java" 146]}],
  :trace
  [[clojure.lang.Var find "Var.java" 146]
   [clojure.core$find_var invokeStatic "core.clj" 2037]
   [clojure.core$find_var invoke "core.clj" 2032]
   [integrant.core$eval8467$fn__8468 invoke "core.cljc" 404]
   [clojure.lang.MultiFn invoke "MultiFn.java" 234]
   [integrant.core$try_build_action invokeStatic "core.cljc" 333]
   [integrant.core$try_build_action invoke "core.cljc" 332]
   [integrant.core$build_key invokeStatic "core.cljc" 341]
   [integrant.core$build_key invoke "core.cljc" 337]
   [clojure.core$partial$fn__5912 invoke "core.clj" 2656]
   [clojure.core.protocols$fn__8249 invokeStatic "protocols.clj" 168]
   [clojure.core.protocols$fn__8249 invoke "protocols.clj" 124]
   [clojure.core.protocols$fn__8204$G__8199__8213
    invoke
    "protocols.clj"
    19]
   [clojure.core.protocols$seq_reduce invokeStatic "protocols.clj" 31]
   [clojure.core.protocols$fn__8236 invokeStatic "protocols.clj" 75]
   [clojure.core.protocols$fn__8236 invoke "protocols.clj" 75]
   [clojure.core.protocols$fn__8178$G__8173__8191
    invoke
    "protocols.clj"
    13]
   [clojure.core$reduce invokeStatic "core.clj" 6887]
   [clojure.core$reduce invoke "core.clj" 6869]
   [integrant.core$build invokeStatic "core.cljc" 365]
   [integrant.core$build invoke "core.cljc" 344]
   [integrant.core$init invokeStatic "core.cljc" 560]
   [integrant.core$init invoke "core.cljc" 552]
   [integrant.core$init invokeStatic "core.cljc" 557]
   [integrant.core$init invoke "core.cljc" 552]
   [integrant.repl$init_system$fn__10153 invoke "repl.clj" 37]
   [integrant.repl$build_system invokeStatic "repl.clj" 24]
   [integrant.repl$build_system invoke "repl.clj" 22]
   [integrant.repl$init_system invokeStatic "repl.clj" 34]
   [integrant.repl$init_system invoke "repl.clj" 33]
   [integrant.repl$init$fn__10165 invoke "repl.clj" 54]
   [clojure.lang.AFn applyToHelper "AFn.java" 154]
   [clojure.lang.AFn applyTo "AFn.java" 144]
   [clojure.lang.Var alterRoot "Var.java" 308]
   [clojure.core$alter_var_root invokeStatic "core.clj" 5536]
   [clojure.core$alter_var_root doInvoke "core.clj" 5531]
   [clojure.lang.RestFn invoke "RestFn.java" 425]
   [integrant.repl$init invokeStatic "repl.clj" 52]
   [integrant.repl$init invoke "repl.clj" 49]
   [integrant.repl$go invokeStatic "repl.clj" 61]
   [integrant.repl$go invoke "repl.clj" 57]
   [integrant.repl$go invokeStatic "repl.clj" 58]
   [integrant.repl$go invoke "repl.clj" 57]
   [com.acme.server.core$start invokeStatic "core.clj" 165]

[ ELIDED ]

  [clojure.lang.RestFn applyTo "RestFn.java" 137]
   [clojure.lang.Var applyTo "Var.java" 705]
   [clojure.main main "main.java" 40]],
  :cause "No such namespace: admin",
  :phase :execution}}