The most common bug in loosely-typed systems is not a wrong answer; it is a wrong value carried far from its birthplace. A string that was supposed to be a path joins a URL; a null flows past three functions before it crashes somewhere innocent. The fix is not more checks; it is types that make the wrong value impossible to construct.
the construction
The rule "model invalid states out of existence" is a type-design rule. If a session can be pending or established but never both, the type should be a union of those states, not a bag of optional fields whose combinations include nonsense. If an operation can only run after verification, the verified value should be a type the unverified one cannot forge: VerifiedEnvelope in vhalla cannot be cloned or constructed outside decode, so "an unverified envelope used as verified" is not a runtime error to catch but a program that does not compile.
This is the deepest form of the lesson: the best error handling is the case you never have to write. Every check you delete by construction is a check that cannot rot.
the parsing convention
Where types cannot reach (the filesystem, the network, a provider response, a stored record), the convention is parse-from-unknown. The foreign value arrives as unknown and earns a type only through a parser that validates shape, rejects unknown keys, and bounds every count, byte size, depth, and list.
Rejecting unknown keys is the load-bearing detail. A parser that accepts {known fields...} + anything silently accepts schema drift: the provider renames id to identifier and the object still parses, now with id === undefined. Rejecting unknown keys converts that drift into a loud boundary error at the exact moment the schema changes. Direct's branded ids follow the same rule one level deeper: a RunId is not a string that happens to look right; it is a brand the parser issues, so a plain string cannot stand in for one.
The bound rule is the same idea applied to size: an unbounded list is a model of "maybe infinite," and systems that accept maybe-infinite input get infinite inputs.
result, not throw
The failure half of the convention is closed results. @hraness/result encodes the pattern the portfolio shares: a function that can fail returns a typed result whose variants enumerate how, instead of throwing into whoever happens to be on the stack. The caller must handle the variants the compiler knows about; an unhandled failure is a type error, not a surprise.
The combination is the actual convention: foreign input parsed at the boundary, invalid states unconstructible inside, failures returned as closed results at the edges. Oh's parsers are the cleanest single example: every stored record and wire value goes through a validator that produces a typed value or a named rejection, and the rest of the kernel never sees the unvalidated shape.