For a syntax-by-syntax review, start with the language walkthrough.
All three blueprints implement the same meaning, disclosure and ownership contract. The vocabulary and examples are comparable; local control flow stays idiomatic.
| Responsibility | Go | Rust | Nest / TypeScript |
|---|---|---|---|
| Construct a classified failure | New(kind, message) |
Failure::new(kind, message) |
failure(kind, message, options) |
| Stable domain identifier | WithType |
with_type |
options.type |
| Public input problems | WithField(s) |
with_field(s) |
options.fields, withFields |
| Private diagnostics | WithDetail(s) |
with_detail(s) |
options.details, withDetails |
| Retain or replace a source | WithCause |
with_source |
options.cause, withCause |
| Add context without translating | fmt.Errorf("operation: %w", err) |
Context::new(operation, err) |
withDetails(err, { operation }) |
| Inspect classification | KindOf |
Classified::classification |
kindOf |
| Project public meaning | Public |
public_info |
publicInfo |
| Inspect diagnostic source | errors.Unwrap/Is/As |
Error::source |
causeOf |
| Carry ordinary outcomes | error / nil |
standard Result<T, E> |
Result<T, E> |
| Match a local condition | sentinel identity with errors.Is |
a domain-owned enum variant | a narrowed kind/type discriminator |
Public projections contain the same four concepts: kind, message, optional domain identifier and field problems. Explicit outer classification owns these together; missing outer metadata is never filled from a cause. Internal and unclassified failures expose only the Internal fallback. Private details and sources remain available for internal inspection.
The example in each build models an email already registered, attaches a private database cause and operation context, and produces this public meaning:
| Value | Result |
|---|---|
| kind | conflict |
| message | email already registered |
| Type | account.email_taken |
| fields.email | taken |
This is a shared semantic shape, not a JSON contract. Go carries an enum Kind,
Rust calls the optional identifier error_type, and TypeScript uses type.
Transport adapters will choose encoding and status/envelope conventions.
- Go's
Publicreturns a presence boolean: false for a literal nil error interface. ItsKindOfboolean instead reports recognized classification. A non-nil unknown failure has a public fallback and no recognized kind. - Rust calls
public_infoon an Err path. An absent classification there means an existing unknown failure. Go nil, Rust Ok and TypeScript{ ok: true }remain success; TypeScript caught null/undefined remain actual failures. - Go copies metadata when deriving errors, Rust moves owned data with consuming builders, and TypeScript copies and freezes records. Foreign causes are opaque.
- Rust delegates classification explicitly through
Classified; a source chain alone does not supply it. TypeScript recognizes module-created failures and carriers without inspecting foreign getters. Go follows a single unwrap chain and stops before aggregate branches. - Error display is diagnostic. Go may include cause text; Rust preserves sources separately; TypeScript uses an Error carrier only at a throwing boundary. Public output always goes through the projection API.
| File | Responsibility |
|---|---|
| kind.go | Stable classification vocabulary |
| errors.go | Error values, condition identity and immutable derivation |
| inspect.go | Single-chain selection and private-frame inspection |
| public.go | Disclosure policy and independent public snapshots |
| doc.go | Package context and architectural contract |
| spec_test.go | E01–E10, Go-specific cases and executable example |
Run go test -race ./... and go vet ./... from the repository root.
Use go test ./pkg/errors -run ExampleError -v for the comparable example.
See the scenario contract and the consistency finding.
DiagnosticTypeOf reads the selected private frame including Internal; TypeOf
and Public retain their existing disclosure policy. See the inspection finding.