malli

Chris Chen 2025-11-27T14:20:51.542149Z

Fairly new to Malli (and Clojure in general), this question might be a bit silly. I have a walker that relaxes certain schema terms to strings (for use in a structured extraction pipeline) - I have some custom types based on malli.experimental.time but the walker seems to panic when it encounters an :iso-duration (defined as either a Period or a Duration). I can't figure out what's causing the infinitely-expanding-schema error. Any help would be much appreciated - code/nREPL outputs below.

clj꞉cpdl.types.registry꞉> (try (generate-loose-registry basic-additions)
                               (catch Exception e
                                 (clojure.pprint/pprint (ex-data e))))
; #IntoSchema {:type :time/duration}
;   ✏️ Relaxing primitive: :uuid-v7 to :string at path: [:source]
;   ✏️ Relaxing primitive: :uuid-v7 to :string at path: [:target]
; #IntoSchema {:type :time/duration}
; #IntoSchema {:type :time/duration}
; #IntoSchema {:type :time/duration}
;   ✏️ Relaxing primitive: :uuid-v7 to :string at path: [:relative 1 :anchor 1]
; 🔍 DIAGNOSTIC [depth 3 ] type: :or | path: [:relative 1 :anchor] | children count: 2 | children: [:and :string] | props: {}
; 🔍 DIAGNOSTIC [depth 3 ] type: :or | path: [:relative 1 :offset] | children count: 2 | children: [:malli.core/schema :malli.core/schema] | props: {}
; ❌ Error loosening type: :cpdl/schedule
; {:type :malli.core/infinitely-expanding-schema,
;  :message :malli.core/infinitely-expanding-schema,
;  :data {:schema [:or :time/duration :time/period]}}
nil
Custom type definitions:
(def iso-duration-schema
  (m/schema [:or :time/duration :time/period] {:registry (merge (m/default-schemas) (met/schemas))}))

(def iso-partial-schema
   (m/schema
    [:and
     [:string {:description "ISO 8601 string with variable precision (YYYY to full instance)"}]
     [:fn {:error/message "Must be a valid ISO 8601 truncated date string"}
      iso-partial?]]))

(def registry
  {:uuid-v7 uuid-v7-schema
   :iso-duration iso-duration-schema
   :iso-partial iso-partial-schema})

(defn merge-with-base
 ([] (merge-with-base (m/default-schemas)))
 ([base-registry]
  (merge base-registry (mu/schemas) (met/schemas) registry)))
Schema walker for reference:
(defn loose-walker [schema path children options]
  (let [t (m/type schema)
        props (m/properties schema)
        loose-registry (:loose-registry options)
        base-registry (:base-registry options)]
    ...
    
    (cond
      ...
      ;; Relax primitives
      (or (uuid-types t) (#{:inst :keyword :iso-partial :iso-duration :time/instant :time/duration :time/period} t))
      (do
        (println "  ✏️ Relaxing primitive:" t "to :string at path:" path)
        [:string (-> props
                     (assoc :loose/changed? true)
                     ;;(update :description #(str (or % "") " (Relaxed from " t ")"))
                     )])

      ...

      ;; Bubble changes in composites/collections
      (#{:merge :union :multi :maybe :ornil :vector :sequential :set :tuple :or} t)
      (let [changed? (bubble-changed? t children)
            new-props (cond-> props changed? (assoc :loose/changed? true))]
        (into [t new-props] children))
      ...

      :else
      (into [t props] children))))

✅ 1
Chris Chen 2025-11-27T14:26:44.489159Z

EDIT: Unwrapping the m/schema call doesn't fix the error but returns an invalid-schema error at the same place 😞

clj꞉cpdl.types.registry꞉> (try (generate-loose-registry basic-additions)
                               (catch Exception e
                                 (clojure.pprint/pprint (ex-data e))))
; #IntoSchema {:type :time/duration}
;   ✏️ Relaxing primitive: :uuid-v7 to :string at path: [:source]
;   ✏️ Relaxing primitive: :uuid-v7 to :string at path: [:target]
; #IntoSchema {:type :time/duration}
; #IntoSchema {:type :time/duration}
; #IntoSchema {:type :time/duration}
;   ✏️ Relaxing primitive: :uuid-v7 to :string at path: [:relative 1 :anchor 1]
; 🔍 DIAGNOSTIC [depth 3 ] type: :or | path: [:relative 1 :anchor] | children count: 2 | children: [:and :string] | props: {}
; 🔍 DIAGNOSTIC [depth 3 ] type: :or | path: [:relative 1 :offset] | children count: 2 | children: [:malli.core/schema :malli.core/schema] | props: {}
; ❌ Error loosening type: :cpdl/schedule
; {:type :malli.core/invalid-schema,
;  :message :malli.core/invalid-schema,
;  :data
;  {:schema [:or :time/duration :time/period], :form [:iso-duration]}}
nil
EDIT 2: Never mind, found the error (it was because I wrapped a keyword in a bracket pair in some of the type definitions where the custom type was called)