malli
malli copied to clipboard
time humanize error brings "unknown error"
I made a schema that is using malli.experimental.time. I check if one key is of either local-datetime zoned-datetime or instant. malli/validate is working as it should , explain does not give back errors, the explained error I get is this: {:exit-date ["unknown error" "unknown error" "unknown error" "unknown error"]}
This is the sourcecode to reproduce the error
(ns quanta.trade.report.roundtrip.validation
(:require
[tick.core :as t]
[malli.core :as m]
[malli.registry :as mr]
[malli.error :as me]
[malli.experimental.time :as time]
;(:import
; (java.time Duration Period LocalDate LocalDateTime LocalTime Instant
; ZonedDateTime OffsetDateTime ZoneId OffsetTime))
)
(def r
(mr/composite-registry
m/default-registry
(mr/registry (time/schemas))))
(def above-zero 0.0000000000000001)
(def Roundtrip
[:map
[:asset :string]
[:side [:enum :long :short]]
[:qty [:double]]
[:entry-price [:double {:min quanta.trade.report.roundtrip.validation/above-zero}]]
[:exit-price [:double {:min quanta.trade.report.roundtrip.validation/above-zero}]]
[:entry-date [:or :time/local-date :time/local-date-time :time/zoned-date-time :time/instant]]
[:exit-date [:or :time/local-date :time/local-date-time :time/zoned-date-time :time/instant]]
[:entry-idx {:optional true} [:int]]
[:exit-idx {:optional true} [:int]]])
(defn validate-roundtrip [rt]
(m/validate Roundtrip rt {:registry r}))
(defn human-error-roundtrip [rt]
(->> (m/explain Roundtrip rt {:registry r})
(me/humanize)))
(comment
;; test with a roundtrip that is ok
(def rt1 {:asset "QQQ"
:side :long
:qty 1.0
:entry-price 105.0
:exit-price 110.0
:entry-idx 15
:exit-date (t/zoned-date-time)
:entry-date (t/date)})
(validate-roundtrip rt1)
;; => true
(human-error-roundtrip rt1)
;; => nil
(human-error-roundtrip
{:asset "QQQ" :side :long
:entry-price 105.0
:exit-price 110.0
:entry-date (t/instant)})
;; => {:qty ["missing required key"], :exit-date ["missing required key"]}
(human-error-roundtrip
{:asset "QQQ" :side :long
:entry-price 105.0
:exit-price 110.0
:entry-idx "asdf"})
;; => {:qty ["missing required key"],
;; :entry-date ["missing required key"],
;; :exit-date ["missing required key"],
;; :entry-idx ["should be an integer"]}
(human-error-roundtrip
{:asset "QQQ" :side :long :qty 1.0
:entry-price 105.0
:exit-price 110.0
:entry-date (t/instant)
:exit-date 3})
;; => {:exit-date ["unknown error" "unknown error" "unknown error" "unknown error"]}
Hi. This is unfortunate. Humanized errors do not have a multimethod to support easy extension of schema-based errors.
With current design, you need to add a property to the schema instance for this:
(defn -local-date-schema [] (-temporal-schema {:type :time/local-date :class LocalDate :type-properties {:min (. LocalDate -MIN) :max (. LocalDate -MAX), :error/message "not a valid
LocalTime instance"}}))
(->> (m/explain (-local-date-schema) 123) (me/humanize))
; => ["not a valid LocalTime instance"]
See #1154
Would you like to add to add the [:type-properties :error/message] data to the time schemas? PR most welcome on this!
I can reproduce with the following code and malli 0.19.1:
(ns repro
(:require
[malli.core :as m]
[malli.error :as me]
[malli.experimental.time :as met]
[malli.experimental.time.transform :as mett]
[malli.registry :as mr]
[malli.transform :as mt]))
(defn f [s]
(let [schema :time/offset-date-time
data (m/decode schema s mett/time-transformer)]
(when-not (m/validate schema data)
(println (me/humanize (m/explain schema data))))
data))
(mr/set-default-registry!
(mr/composite-registry
(m/default-schemas)
(met/schemas)))
(f "2025-10-31T14:28:12Z")
;=> #object [java.time.OffsetDateTime 0x2dcd71e6 "2025-10-31T14:28:12Z"]
(f "2025-10-31T14:28:Z")
; [unknown error]
;=> "2025-10-31T14:28:Z"
Ideally, I would expect malli to report something like "string does conform to ISO 8601 date time extended format" -- i.e. I'd like to be able to get information about the string before transformation.
Currently I use a hand-crafted regexp to validate manually before transformation, to workaround this:
(def offset-date-time-string
"Malli schema for ISO 8601 extended format date time"
[:re {:error/message "should be an ISO 8601 extended format date time"}
#"^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(\.\d\d\d)?([+-][0-2]\d:[0-5]\d|Z)$"])
(me/humanize (ma/explain offset-date-time-string "2025-10-31T14:28:Z"))
;=> ["should be an ISO 8601 extended format date time"]