Skip to content

Stability Guarantees

Lanexio Parser makes three public stability commitments. They are tested in CI, backed by fuzz harnesses, and enforced by architectural constraints. Breaking any one of them is a P0 bug.

parse(), reparse(), streaming parse methods, and the serializers (serializeHtml, serializeMarkdown) never throw. Malformed input produces error nodes in the AST. Exceptions never leave the parse boundary.

import { parseHtml } from '@lanexio/parser-grammar-html';
// This never throws, even on deeply malformed input.
const encoder = new TextEncoder();
const tree = parseHtml(encoder.encode('<<<<<\x00\xFF\xFE'));
// Check for parse errors in the tree.
for (const node of tree.root.children()) {
if (node.hasError) {
console.log('parse error at', node.range);
}
}
  • You do not need to wrap parseHtml or parseMarkdown in a try-catch.
  • Malformed input is always safe to pass to the parser.
  • The return value is always a valid LexTree.
  • The returned tree may contain LexError nodes. It is your responsibility to detect and handle them.
  • Third-party code you call with the parse output (e.g., innerHTML = serializeHtml(tree)) may behave unexpectedly on malformed input. That is outside the parse boundary.

Guarantee 2: Panic-Free Byte Sequence Immunity

Section titled “Guarantee 2: Panic-Free Byte Sequence Immunity”

No byte sequence of any length causes a panic, unhandled exception, or silent memory corruption.

This guarantee is backed by:

  • Iterative state machines — no recursion in parsers or serializers; 50,000-level-deep nesting is covered by regression tests for both serializeHtml and serializeMarkdown
  • Structural forward-progress guards in loop-driven state machines (a dispatch gap degrades to an error node plus a one-byte advance, never a hang)
  • Wrapping arithmetic operators in all Zig hot paths
  • Bounds checking before every slice index on user input
  • 24-hour continuous fuzz soak on every release

At tag v1.0.0, a 24-hour continuous fuzz soak ran 13 harnesses across all grammar packs:

HarnessCases / 24hResult
core-soak (parse)594,620,6740 crashes
html-char-eof3,829,498,4860 crashes
html-fragment-ns3,278,712,4340 crashes
html-frameset3,185,115,0650 crashes
html-aa-byte-range1,899,683,0160 crashes
html-foster-parent1,413,404,0610 crashes
html-aa-tree-shape1,179,886,4460 crashes
json-parse1,065,749,1990 throws
yaml-parse970,495,7530 throws
html-orphan-p823,743,5730 crashes
html-rawtext-nul604,480,3590 violations
css-parse403,933,2930 throws
html-noscript247,989,7170 violations
Total~20,000,000,0000 failures
  • You can pass any arbitrary byte sequence (null bytes, invalid UTF-8, binary data, adversarial inputs) to parseHtml or parseMarkdown without crashing the process.
  • The output is always a valid LexTree with the appropriate LexError nodes.
  • The output may be unusable for its intended purpose if the input is deeply corrupted. The parser does its best, but tree shape is undefined for extreme malformations.
  • Performance is not guaranteed on adversarial input. The parser may run slower on inputs designed to maximize backtracking.

Guarantee 3: Deterministic 3-Token Recovery Window

Section titled “Guarantee 3: Deterministic 3-Token Recovery Window”

After a syntax error, the parser resyncs to a valid construct within 3 tokens.

This means:

  • Parse errors do not cascade indefinitely.
  • The tree shape after an error is bounded and predictable.
  • For any given input, parseHtml(input) always produces the same LexTree (deterministic output).
  • Calling parseHtml(input) twice with the same input always returns an equivalent tree.
  • Error recovery is local and bounded.
  • The recovered subtree is necessarily semantically correct. The parser resumes at a valid grammar point, but the semantic meaning of the recovered tree depends on the document.
  • The 3-token window applies to spec-defined recovery paths. Certain error classes (e.g., deeply nested malformed trees) may have documented exceptions. See zig/grammars/*/recovery.zig for grammar-specific notes.
GuaranteeWhat it coversHow it is enforced
Never-throwparse(), reparse(), streaming methodspnpm verify:no-throw, Vitest never-throw harness
Panic-freeAny byte sequence inputZig fuzz harnesses, zig build fuzz
3-token recoveryBounded error recovery in parse treesCorpus entries in corpus/malformed/, recovery.zig