Welcome! Please see the About page for a little more info on how this works.

+1 vote
in Errors by

I'm using s/explain-data within if-let to decide in one pass whether some data is valid or whether I should throw an exception:

(if-let [explanation (s/explain-data spec x)]
(throw (ex-info "Invalid" explanation))

When handled by my Cursive repl, this is rendered as

Execution error - invalid arguments to name.space/function-name at (source.clj:nn)
...then the explain-str form of the error

My problem is that the message is misleading—there could be reasons other than "invalid arguments" that the data were invalid! Wouldn't it be better if the text was simply:

Execution error - data validation failed within name.space/function-name at (source.clj:nn)

1 Answer

+1 vote
edited by

What's happening here is that you are accidentally creating the same exception object pattern that spec creates when there is a function spec failure during instrumentation (specifically a root cause ex-info whose ex-data is spec explain data). This is handled as a special case in the error printer.

A workaround would be to avoid that pattern, for example by moving that data out of the root:

(ex-info "Data validation failed" {:invalid (s/explain-data int? :a)})

I will think more about whether we can be more choosy about when this case is triggered in the error printer.

Since "magic" is happening here, would it be better for instrumentation failures to use a hash map of ex-data with a :: key that is specific to instrumentation, so the "magic" processing of that never triggers for "normal" user exceptions?

It seems like throwing ex-info where the data is from s/explain-data might be pretty common in user code?
Yeah that sounds good Sean. Thank you both for super-rapid feedback!