Error Diagnostics

Every error in mino produces a structured diagnostic: a plain map with stable keys, a classified error code, source location, and a human-readable message. The same model serves the REPL, embedded hosts, and user code.

What you see

When an error occurs in the REPL or a script, mino renders a diagnostic with the error code, message, source file, line, column, and a snippet of the source with a caret pointer:

error[MTY001]: count: expected a collection, got int
  --> app.clj:18:3
   |
 18 |   (count 42)
    |   ^

Reader errors show the exact position:

error[MRE001]: unterminated string literal
  --> app.clj:7:8
   |
  7 | (def y "unterminated
   |        ^

Errors are data

In catch handlers, the exception value is always a map with these keys:

KeyTypeMeaning
:mino/kindkeywordCategory: :reader, :syntax, :eval/type, :eval/arity, :eval/bounds, :user, etc.
:mino/codestringStable error code, e.g. "MTY001"
:mino/phasekeywordProcessing phase: :read, :eval
:mino/messagestringHuman-readable primary message
:mino/dataanyThe original thrown value (for user exceptions)

Example: catching a type error and inspecting it.

(try
  (count 42)
  (catch e
    (println (:mino/kind e))    ;; :eval/type
    (println (:mino/code e))    ;; "MTY001"
    (println (:mino/message e)) ;; "count: expected a collection, got int"
    ))

throw and catch

(throw x) accepts any value. The catch handler always receives a diagnostic map, regardless of what was thrown. The original value is accessible via (ex-data e):

(try
  (throw "oops")
  (catch e
    (println (ex-data e))    ;; "oops"
    (println (ex-message e)) ;; "oops"
    (println (error? e))     ;; true
    ))

If you throw an ex-info map, ex-data and ex-message extract the payload transparently:

(try
  (throw (ex-info "not found" {:code 404}))
  (catch e
    (println (ex-data e))    ;; {:code 404}
    (println (ex-message e)) ;; "not found"
    ))

Helper functions

FunctionDescription
(error? x)True if x is a map with :mino/kind
(ex-data e)Extract the data payload from a diagnostic map or ex-info
(ex-message e)Extract the message from a diagnostic map or ex-info
(last-error)Return the last diagnostic map, or nil
(ex-info msg data)Create an exception map for throwing

Error code catalog

Every error has a stable code that can be matched programmatically. Codes are grouped by prefix:

PrefixCategoryExamples
MREReaderUnterminated string, unexpected delimiter, unterminated collection
MSYSyntaxMalformed def, fn, let special forms
MNSName resolutionUnresolved symbol, missing namespace alias, capability-disabled name
MARArityWrong number of arguments
MTYType mismatchOperation received unsupported value type
MBDBoundsIndex out of range
MCTContractPrecondition or invariant violation
MSTStateCross-state misuse, transaction-state error, STM retry exhaustion
MHOHost interopHost callback failure
MTHThreadThread-limit exceeded
MIOI/Oslurp, spit, sh, file-system access failures
MOMOut of memoryAllocator exhausted during a user-visible computation (e.g. bignum arithmetic)
MOVOverflowInteger overflow on + / - / * / inc / dec when the bignum-promoting variant (+' etc.) was not used
MLMLimitStep limit, heap limit, recursion depth exceeded
MUSUserUser-thrown exception
MINInternalRuntime invariant violation or unhandled internal-state error

C API

Embedders have structured access to the last error:

/* Backward-compatible string access */
const char *mino_last_error(S);

/* Structured diagnostic (internal fields) */
const mino_diag *mino_last_diag(S);

/* Diagnostic as a mino map with :mino/* keys */
mino_val *mino_last_error_map(S);

/* Render to buffer in compact or pretty mode */
int mino_render_diag(S, diag, MINO_DIAG_RENDER_PRETTY, buf, sizeof(buf));

Existing code using mino_last_error() continues to work unchanged. The string is rendered from the structured diagnostic internally.