Skip to content

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:

  1. Maintain your own exception hierarchy or error enum, with names meaningful to your language.
  2. Write a thin translation function (like the errorCode property on KbException in the Kotlin reference) that maps each internal failure type to its KB-NNNN string.
  3. In your test runner, call that function on the actual failure and compare it to the fixture's errorCode (hard failures) or expectedSoftFails set (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.