Error Codes¶
kBars identifies every classified failure with a stable KB-NNNN code. These codes appear in
conformance fixture JSON as errorCode (hard failures), expectedSoftFails entries (render-suite
soft failures), and the per-event errorCode on a partials-suite softFails[] entry — and on the
errorCode property of thrown KbException subclasses and KbSoftFailEvent.Category values in the
reference implementation. Human-readable exception messages are non-normative and may change between
releases; codes are stable across kBars versions.
A code appears in a conformance fixture only when there is a specific, testable meaning — not merely to record that some error occurred. Fixtures that do not yet carry a code, or that assert only that some failure occurs, remain valid; the absence of a code is not itself an error.
The table below is also published as machine-readable data at conformance/error-codes.json
(schema: conformance/schema/error-codes.schema.json), so a porter can generate an error enum or
validate fixtures against known codes without scraping this page or running any Kotlin. It ships in
the same distribution ZIP as the fixtures. A :kbars-conformance drift test
(ErrorCodeCatalogueSuite) asserts every code cited anywhere in a fixture's raw JSON — whether via
errorCode, expectedSoftFails, or a partials-suite softFails[].errorCode entry — and every public
KbSoftFailEvent.Category code, appears in the registry — a containment check, not exact equality,
since the registry may additionally carry reserved codes that no fixture exercises.
Code ranges¶
| Range | Family | Kind |
|---|---|---|
KB-0000 |
Unclassified / fallback | hard |
KB-1xxx |
Grammar / parse errors | hard |
KB-2xxx |
Transform errors | hard |
KB-3xxx |
Assign-tag errors | hard |
KB-4xxx |
Partials errors | hard |
KB-5xxx |
Soft-fail categories | soft |
Hard failures throw a KbParseException or KbRenderException (both extend KbException);
rendering stops. Soft failures do not throw — the engine recovers by evaluating to null /
empty and reports a KbSoftFailEvent to any registered KbEventListener; rendering continues.
Error code table¶
| Code | Family | Reserved? | Meaning |
|---|---|---|---|
KB-0000 |
unclassified | ✅ reserved | Default errorCode on KbException when no specific code was assigned. No conformance fixture uses KB-0000 as an expected code — treat it as an opaque signal that some error occurred, with the message text as the only diagnostic. |
KB-1001 |
grammar | Catch-all for template source rejected by the ANTLR grammar. Several runtime validity checks (e.g. bare non-namespaced {{= targets) are also caught at parse time, so KB-1001 may fire for conditions that look like runtime errors. |
|
KB-1002 |
grammar | The parser requires at least one space between {{ and the first token, and between the last token and }}; fires when that whitespace is absent. |
|
KB-1003 |
grammar | ✅ reserved | A template's block nesting exceeds the implementation's maximum depth, checked before the recursive-descent parser runs. The depth is implementation-defined, so no portable fixture pins it; kBars verifies it with internal tests. |
KB-2001 |
transform | A transform name used in the template is not registered in the rendering environment. | |
KB-2002 |
transform | A built-in transform was called with the wrong number of arguments. | |
KB-3001 |
assign | ✅ reserved | A {{= assign tag's target is neither @env/name nor @local/name. Pre-empted at parse time by KB-1001 for every currently-known invalid target form, so no fixture currently exercises it at runtime; reserved for any implementation path that detects the invalid target after the parse phase. |
KB-3002 |
assign | A {{= assign tag's target carries a sub-pointer (e.g. @env/a/b) — only bare @env/name and @local/name targets are valid. |
|
KB-4001 |
partials | A static {{> key }} tag names a partial key the registered loader does not know (the loader returned null). Because the key is fixed in the template source, this is a hard failure. |
|
KB-4002 |
partials | A {{> … }} tag was encountered but no onLoadPartial loader has been registered on the rendering environment. |
|
KB-5001 |
softfail | TypeMismatch — an input value or transform argument was the wrong type or shape for the operation. |
|
KB-5002 |
softfail | DegenerateValue — the input type was valid but the value is degenerate for the operation (e.g. a zero divisor, or an empty collection passed to an operation requiring at least one element). |
|
KB-5003 |
softfail | UnparseableInput — a string value was the correct type but could not be parsed into the required sub-type (e.g. a malformed date string, a malformed percent sequence). |
|
KB-5004 |
softfail | ComparisonMismatch — a relational comparison (<, <=, >, >=) received operands of incompatible types and fell back to false. |
|
KB-5005 |
softfail | UnresolvedReference — the template referenced an identifier (via {{ name }} or @env/name) that neither the context nor the environment provides. |
|
KB-5006 |
softfail | NoMatch — a search transform (find, find_index) found no element matching the predicate. Emitted at Info severity because a miss is a normal, expected outcome. |
|
KB-5007 |
softfail | RecursionLimit — a {{> partial }} chain exceeded the configured maximum partial-render depth. The over-deep partial is abandoned and rendering continues; the event carries the abandoned partial key. |
How to map codes to your error type¶
A port does not need to use these strings internally. The codes exist only at the conformance-fixture boundary. The typical pattern is:
- Maintain your own exception hierarchy or error enum, with names meaningful to your language.
- Write a thin translation function (like the
errorCodeproperty onKbExceptionin the Kotlin reference) that maps each internal failure type to itsKB-NNNNstring. - In your test runner, call that function on the actual failure and compare it to the fixture's
errorCode(hard failures) orexpectedSoftFailsset (soft failures).
When a fixture's errorCode is absent, accept any thrown failure without comparing codes. When
expectedSoftFails is absent, do not assert on soft-fail events at all.
Soft failures are a design choice, not an accident of the reference implementation: a port may
legitimately choose to throw instead of soft-failing for a KB-5xxx condition, but doing so means
diverging from the reference's recover-and-continue rendering behavior, not just its error
reporting — the fixture's expectedOutput assumes the render completed.