Stable surface from v0.1.0 onward. Changes require a MAJOR version bump per ROADMAP §1.4 SemVer rule.
This document specifies the two env vars that govern how cljw
renders runtime errors. Implementation lives in
src/app/error_render.zig; tests at
test/e2e/phase14_error_format.sh.
Selects the stderr render format.
| Value | Behavior |
|---|---|
unset / text |
Human-readable carat-pointer format. Source line + arrow indicating column + kind label + message. Default for terminal use. |
edn |
Single-line structured EDN map (see schema below). Suitable for editor / CIDER / log-aggregator integration + cljw render-error post-mortem decode. |
| anything else | Falls back to text (typo'd values do not break user output). |
{:cljw/error true
:kind <keyword> ; catalog error: one of :syntax_error :number_error :string_error
; :name_error :arity_error :value_error :not_implemented :type_error
; :arithmetic_error :index_error :cancellation_error :state_error
; :io_error :file_not_found :stack_overflow_error :internal_error
; (+ the internal-condition Kinds :out_of_memory /
; :resource_exhausted / :cancellation_abort, which rarely reach
; the renderer — a resource breach / worker cancel unwind)
; user (throw v): :exception
:phase <keyword> ; one of: :parse :analysis :macroexpand :eval
; (tokenize-level errors render under :parse)
:file <string> ; source file path or synthetic label ("<-e>", "<stdin>", "<repl:N>")
:line <int> ; 1-based; 0 if unknown
:column <int> ; 1-based; 0 if unknown
:message <string> ; rendered message (ex-message for a thrown ex-info; the
; printed value for a non-ex-info throw)
:data <edn> ; (thrown ex-info only) the ex-data map; absent otherwise
}- One event per error, terminated with a single newline.
- The
:cljw/error truediscriminator MUST appear first so the event survives stdout/stderr interleaving in mixed log output. - Strings are EDN-escaped:
"→\",\→\\,\n→\\n. - A user
(throw v)renders:kind :exception(it raises a thrown Value, not a catalog error). For(throw (ex-info msg data)),:messageismsgand:dataisdata; for any other thrown Value (e.g.(throw 42)),:messageis the printed value and:datais absent. Anycljw.error/*error-context*bound viawith-contextat throw time merges in as top-level fields, same as the catalog path.
- Future keys MAY be added (e.g.
:cause,:trace); a reader MUST tolerate unknown keys. - The
:kindvalues and 4:phasevalues listed above are stable; new variants ride a MAJOR bump. v0.1.0 shipped 12 catalog kinds; the v1.0.0 MAJOR added 7 (:cancellation_error/:state_error/:file_not_found/:out_of_memory/:resource_exhausted/:stack_overflow_error/:cancellation_abort), for 19 catalog +:exceptiontoday. A reader MUST tolerate an unknown:kind. :file/:line/:columncarry the analyzer's known location; today some locations surface as"unknown"/0/0(the analyzer doesn't yet populate the field from the per-call source-context). Phase 14.13 v0.1.0 release polish closes this gap; the field shape is stable.
When set to a filesystem path, the EDN-rendered error event is
appended to that file IN ADDITION to the stderr render. The log
file always carries the structured event regardless of
CLJW_ERROR_FORMAT — text-mode users still get a machine-
parseable post-mortem trail.
- File is opened with
truncate=false; existing content is preserved. - Writes go at
file.length(io)viawritePositionalAllso concurrent appends from sibling processes do not truncate each other. - The file is created if absent. Permission errors / disk-full / path-not-found surface as silently-dropped writes — the primary stderr render is unaffected.
- No log rotation, no size cap, no compression. Use external
tooling (
logrotate(8)/ Vector / Fluent Bit) if needed.
# Production process, structured logs only on disk.
CLJW_ERROR_LOG=/var/log/cljw/errors.edn cljw app.clj
# Interactive REPL with EDN events for tail-friendly tooling.
CLJW_ERROR_FORMAT=edn cljw repl
# Both: stderr is human-readable, log file collects structured events.
CLJW_ERROR_LOG=./errors.edn cljw -e '(some-form)'
# Post-mortem decode (future, D-100c row 14.11).
cljw render-error ./errors.ednNot exposed today. The env vars are read once by the cljw CLI
dispatcher at startup; the format selection is process-wide. A
future (cljw.error/get-format) / (cljw.error/set-format!) API
may surface for runtime control — out of scope for v0.1.0.
The env var names (CLJW_ERROR_FORMAT / CLJW_ERROR_LOG),
the format value set (text / edn), the EDN event
discriminator key (:cljw/error true), and the 19 catalog
:kind values (+ :exception) + 4 :phase values above are stable.
Changes require a MAJOR version bump per ROADMAP §1.4 (the v1.0.0
MAJOR is where the 7 post-v0.1.0 kinds were added).
.dev/debt.yamlD-066 (this surface's tracking row).src/app/error_render.zig(implementation).test/e2e/phase14_error_format.sh(7 e2e cases).- ROADMAP §9.16 row 14.13 (v0.1.0 polish bundle).
- ROADMAP §1.4 (SemVer rule for surface stability).