Skip to content

Envelope Schema

exception_to_dict() and exception_to_json() have produced envelopes since 1.2.0, and the payload grew as the library did: exceptions for group members in 1.3.0, the failure object in 1.4.0. All of it was described only in prose. Prose is not something a Node.js, Go or Rust consumer can test against, and a field whose meaning is implied by one implementation drifts the moment that implementation changes.

The envelope is therefore published as a JSON Schema, versioned independently of the package: it describes the payload, not the release that emitted it.

  • Schema: envelope-1.0.0.json
  • $id: https://diogoribeiro7.github.io/DataExcept/schema/envelope-1.0.0.json
  • Dialect: JSON Schema draft 2020-12

Reading the schema from Python

from dataexcept import ENVELOPE_SCHEMA_ID, ENVELOPE_SCHEMA_VERSION, envelope_schema

schema = envelope_schema()

envelope_schema() returns a fresh copy each call, so registering it with a validator or embedding it in an OpenAPI document cannot corrupt the copy the next caller gets.

DataExcept does not validate its own output at runtime and takes no dependency on a validator. The package produces envelopes and publishes the contract; the checking belongs to whoever consumes them, in whichever language.

The shape

An envelope node is one of three kinds, told apart by shape alone: an exception record, the cycle record standing in for an exception already on the path from the root, or the truncation marker replacing a child past the depth budget. Both markers are exact, and the exclusion runs both ways: a validator rejects either marker carrying anything beyond the fields listed for it, and rejects a record rendered in full that carries cycle or truncated at all.

Field Type Meaning
type string The exception class name, unqualified. Always present.
module string The module defining that class. With type it identifies the class; it is not an import instruction. Always present.
message string The rendered message, with credential-bearing URLs redacted. Always present.
attributes object Public instance attributes, JSON-safe. Absent when there are none, or when include_attributes=False.
failure object Recovery metadata. Present on exceptions DataExcept classifies, absent on third-party ones.
cause envelope The explicitly chained exception, from raise ... from ....
context envelope The implicitly chained exception, from raising inside an except block. Absent when the context was suppressed.
exceptions array of envelopes Members of an exception group. Absent — rather than empty — for an ordinary exception.
cycle true Marks a cycle record: this exception is already on the path from the root. Such a record carries type, module, message and this field and nothing else — no cause, context, members or attributes — so following a chain always terminates.
truncated true The truncation marker, and the only field it carries. The record it stands for was never rendered.

The failure object always carries all three of its fields:

Field Type Meaning
kind transient, permanent or unknown Whether the condition is known to be temporary, permanent for the same operation and payload, or unclassified.
retryable boolean or null Whether retrying can succeed. null means no classification is warranted, which is deliberately distinct from false.
retry_after_seconds number or null How long to wait, when the backend said. Finite and non-negative.

See Failure Metadata for how those values are chosen, and Advanced Usage for the producing side.

Fixtures

Every fixture below is the output of running the real serializer over a real exception, so it cannot describe an envelope the library does not emit. They are the reference payloads for a reader written in another language.

Fixture Covers
ordinary-exception.json Identity, message, attributes and a default failure record.
explicit-cause.json A DataExcept exception wrapping a third-party one. The cause has no failure field.
failure-metadata.json Backend-informed metadata overriding the class default, including a retry delay.
implicit-context.json A failure raised while handling another, without from.
nested-exception-group.json Concurrent failures kept as a tree, with a group inside a group.
redaction.json Credentials removed from the message, the attributes and the cause alike.
truncation.json A chain longer than max_depth, ending in the truncation marker.
cycle.json A chain that loops back on itself, ending in the cycle marker.

Regenerate them with:

python scripts/generate_envelope_fixtures.py

CI fails if the committed files no longer match what the serializer emits, so running that script is how a change to the envelope is accepted rather than how it is discovered.

What the version promises

The schema version tracks the envelope, not the package. It moves when the payload changes, so a later DataExcept release that touches nothing in the envelope still serves envelope-1.0.0.json.

Within 1.x:

  • a later version may add fields;
  • an established field does not silently change meaning, type or nullability;
  • a field that is absent stays absent for the reason documented above — failure on a third-party exception, exceptions on a non-group — so a consumer can read absence as information.

Consumers must ignore fields they do not recognise. That is what makes the first rule safe, and it is the only way a payload from a newer producer stays readable by an older reader.

Consuming this from Node.js

The envelope is the canonical contract, but a logger downstream may want the same failure under the names it already keys on. See Pino interoperability for the published projection, its own schema, and a fixture pair for every case above.