Skip to documentation

SDK and error contracts

Quick start

Quick start How do you emit your first envelope? Pin the package at an exact commit, import your runtime, and build one failure. Every runtime is dependency free on purpose — no serde, no validator, no npm tree — so adoption costs one depen

Quick start

How do you emit your first envelope? Pin the package at an exact commit, import your runtime, and build one failure. Every runtime is dependency-free on purpose — no serde, no validator, no npm tree — so adoption costs one dependency line and nothing else.

Pin the commit

Every consumer names the exact revision in its own dependency spec, not just in a lockfile:

npm    "@wisent/errors": "github:wisent-ai/wisent-errors#<sha>"
cargo  wisent-errors = { git = "https://github.com/wisent-ai/wisent-errors", rev = "<sha>" }
pip    wisent-errors @ git+https://github.com/wisent-ai/wisent-errors@<sha>#subdirectory=python
spm    .package(url: "https://github.com/wisent-ai/wisent-errors", revision: "<sha>")

The manifests sit where each tool expects them: package.json and Package.swift at the repository root, a root Cargo workspace naming rust/, and the Python package under python/ (hence the subdirectory). A lockfile stops a checkout from drifting; it does not stop the command a person types, which is why the pin lives in the spec.

JavaScript

import { failure, raise, render } from '@wisent/errors';

const envelope = failure({
  failurePoint: 'brama.dispatch.bounded-rotation',
  code: 'rate_limit',
  service: 'brama',
  impact: 'one model request',
  detail: providerText,
});
console.error(render(envelope));

failure returns the envelope as a plain object with severity, retryable, and outage filled in from the catalogue. raise throws the same envelope wrapped in a FailureError; render returns one human sentence followed by the envelope JSON on the same line, so grep finds both. TypeScript declarations ship in js/index.d.ts.

Python

from wisent_errors import failure, raise_failure, render

envelope = failure(
    failure_point="stado.cli.registry-pull",
    code="infra_down",
    service="stado",
    impact="the registry read this command needed",
    detail=str(error),
)
print(render(envelope))

All arguments are keyword-only. raise_failure raises a FailureError carrying the envelope.

Rust

use wisent_errors::{Code, Failure};

let refused = Failure::new("brama.gateway.oauth-refresh", Code::Auth, "brama")?
    .impact("one credential refresh")
    .detail(provider_text)
    .with_context("subscription", id);
eprintln!("{}", refused.render());

Failure::new returns Result<Failure, Invalid>; the builder methods impact, detail, caused_by, and with_context fill the rest. Failure implements Display and std::error::Error, and to_json() serializes with the schema's key order.

Swift

import WisentErrors

let refused = try Failure(failurePoint: "accounts.create", code: .notFound, service: "growth-tactics")
    .detail(providerText)
print(refused.render())

The Swift package also ships WisentFailureReporter, a Probierz intake transport for desktop components; see runtimes.

Inside an error path, never throw

failure / Failure::new are strict and reject malformed input, which is right at a call site that wants to fail loud. Inside an error path — where an exception destroys the diagnosis being carried — use the salvage builders: failureOrFallback (JS), failure_or_fallback (Python), Failure::or_fallback (Rust), Failure.orFallback (Swift). They never fail: an absent point becomes unknown, a malformed one is kept verbatim, and each violation is recorded in context under a wisent_errors. key, so the defect travels in the data instead of becoming an exception raised inside a catch.

Prove the copy you pinned

From a clone, one command runs everything that must hold before the package ships — generated code matches the catalogue, schema and catalogue agree, every golden envelope obeys the schema, and the four runtimes agree byte for byte:

node ci/check.mjs

It needs node, python3, cargo, and swift on the machine, because the harness builds and runs all four emitters; see conformance. The seven codes and everything derived from them are in catalogue; the adoption path for a whole product is integration.