Skip to content

sartoriuslib.errors

Typed exception hierarchy and structured ErrorContext. See Troubleshooting for a field-guide cheat sheet and Design §12 for the full tree.

sartoriuslib.errors

Typed exception hierarchy for :mod:sartoriuslib.

Every library exception inherits from :class:SartoriusError and carries a structured :class:ErrorContext. See design doc §12.

In addition to exceptions, the library emits :class:SartoriusCapabilityWarning (a :class:UserWarning subclass) when a command is attempted against a device whose priors do not match in non-strict mode (design doc §6.1).

ErrorContext dataclass

ErrorContext(
    command_name=None,
    command_bytes=None,
    opcode=None,
    sbi_token=None,
    raw_response=None,
    protocol=None,
    port=None,
    model=None,
    family=None,
    sbn_address=None,
    elapsed_s=None,
    extra=_empty_extra(),
)

Structured context attached to every :class:SartoriusError.

Fields are best-effort — missing data is None rather than raising.

extra accepts any Mapping and is always frozen into a read-only :class:types.MappingProxyType at construction so the shared empty sentinel can never be mutated through error.context.extra[k] = v.

address property

address

Unified cross-library accessor for the device address.

For sartoriuslib this is the xBPI SBN address (sbn_address). Consumers that work across sibling libraries (alicatlib, watlowlib, nidaqlib) read ctx.address uniformly; sartorius-internal code keeps using sbn_address because it carries protocol-layer semantics.

merged

merged(**updates)

Return a new context with updates overlaid. Unknown keys go to extra.

Source code in src/sartoriuslib/errors.py
def merged(self, **updates: Any) -> Self:
    """Return a new context with ``updates`` overlaid. Unknown keys go to ``extra``."""
    known: dict[str, Any] = {}
    extra_updates: dict[str, Any] = {}
    for key, value in updates.items():
        if key in _CONTEXT_KNOWN_FIELDS:
            known[key] = value
        else:
            extra_updates[key] = value

    new_extra: Mapping[str, Any] = (
        MappingProxyType({**self.extra, **extra_updates}) if extra_updates else self.extra
    )
    return replace(self, **known, extra=new_extra)

InvalidParameterIndexError

InvalidParameterIndexError(message='', *, context=None)

Bases: SartoriusConfigurationError

Parameter-table index is out of range for this device.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

InvalidSbnError

InvalidSbnError(message='', *, context=None)

Bases: SartoriusConfigurationError

SBN bus address is invalid.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusAutoprintActiveError

SartoriusAutoprintActiveError(message='', *, context=None)

Bases: SartoriusProtocolError

SBI autoprint is active, so command/reply traffic is not reliable.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusCapabilityError

SartoriusCapabilityError(message='', *, context=None)

Bases: SartoriusError

Command is not available on this device / firmware / family.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusCapabilityWarning

Bases: UserWarning

Emitted when a command's family/capability priors do not match the device.

In non-strict mode (the default) the library attempts the command anyway and updates availability from the device's response. See design doc §6.1.

SartoriusCommandRejectedError

SartoriusCommandRejectedError(message='', *, context=None)

Bases: SartoriusProtocolError

The device returned an xBPI subtype 0x01 / SBI refusal response.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusConfigurationError

SartoriusConfigurationError(message='', *, context=None)

Bases: SartoriusError

Configuration-level error (bad args, wrong confirm flag, etc.).

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusConfirmationRequiredError

SartoriusConfirmationRequiredError(
    message="", *, context=None
)

Bases: SartoriusConfigurationError

A PERSISTENT / DANGEROUS command was attempted without confirm=True.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusConnectionError

SartoriusConnectionError(message='', *, context=None)

Bases: SartoriusTransportError

Could not open / lost the connection to the balance.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusError

SartoriusError(message='', *, context=None)

Bases: Exception

Base class for every :mod:sartoriuslib exception.

Carries a typed :class:ErrorContext. The message is the human-readable summary; the context is the machine-readable detail.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

with_context

with_context(**updates)

Return a copy of this error with its context updated.

Useful when an inner layer raises and an outer layer wants to enrich the context (for instance adding port or elapsed_s).

Source code in src/sartoriuslib/errors.py
def with_context(self, **updates: Any) -> Self:
    """Return a copy of this error with its context updated.

    Useful when an inner layer raises and an outer layer wants to enrich
    the context (for instance adding ``port`` or ``elapsed_s``).
    """
    cls = type(self)
    new = cls.__new__(cls)
    new.args = self.args
    try:
        new.__dict__.update(self.__dict__)
    except AttributeError:  # pragma: no cover — no slotted subclass today
        for slot in getattr(cls, "__slots__", ()):
            if hasattr(self, slot):
                object.__setattr__(new, slot, getattr(self, slot))
    new.context = self.context.merged(**updates)
    new.__cause__ = self.__cause__
    new.__context__ = self.__context__
    new.__traceback__ = self.__traceback__
    return new

SartoriusFirmwareError

SartoriusFirmwareError(message='', *, context=None)

Bases: SartoriusCapabilityError

Command is outside the supported firmware window.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusFrameError

SartoriusFrameError(message='', *, context=None)

Bases: SartoriusProtocolError

Bad checksum, bad length, malformed TLV, etc.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusIndexOutOfRangeError

SartoriusIndexOutOfRangeError(message='', *, context=None)

Bases: SartoriusCapabilityError, SartoriusCommandRejectedError

Device returned xBPI err 0x10 (index out of range).

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusMissingArgsError

SartoriusMissingArgsError(message='', *, context=None)

Bases: SartoriusCapabilityError, SartoriusCommandRejectedError

Device returned xBPI err 0x07 (invalid or missing args).

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusOperationNotApplicableError

SartoriusOperationNotApplicableError(
    message="", *, context=None
)

Bases: SartoriusCapabilityError, SartoriusCommandRejectedError

Device returned xBPI err 0x06 (operation not applicable).

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusParseError

SartoriusParseError(message='', *, context=None)

Bases: SartoriusProtocolError

Unknown xBPI subtype or unparseable SBI line.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusProtocolError

SartoriusProtocolError(message='', *, context=None)

Bases: SartoriusError

Protocol-level error (framing, parsing, device refusal).

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusProtocolUnsupportedError

SartoriusProtocolUnsupportedError(
    message="", *, context=None
)

Bases: SartoriusProtocolError

Command has no variant defined for the active protocol.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusSinkDependencyError

SartoriusSinkDependencyError(message='', *, context=None)

Bases: SartoriusSinkError, SartoriusConfigurationError

A sink's optional backing library is not installed.

Raised when the user instantiates (or calls open() on) a sink whose extras have not been installed — e.g. ParquetSink without sartoriuslib[parquet] or PostgresSink without sartoriuslib[postgres]. The message names the exact extra to install so the remediation is copy-pasteable.

Multi-inherits :class:SartoriusConfigurationError because callers that already branch on configuration errors (missing extras being a configuration problem from their perspective) keep working without changes.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusSinkError

SartoriusSinkError(message='', *, context=None)

Bases: SartoriusError

Base class for errors raised by sinks (CSV, JSONL, SQLite, Parquet, Postgres).

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusSinkSchemaError

SartoriusSinkSchemaError(message='', *, context=None)

Bases: SartoriusSinkError

A batch's shape is incompatible with the sink's locked schema.

Raised when a sink has locked its schema on the first batch (or validated against an existing table) and a subsequent batch carries rows whose shape can't be reconciled — for example, a Postgres target table that's missing a required column.

Dropping unknown optional columns is handled by a per-sink WARN log and does not raise.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusSinkWriteError

SartoriusSinkWriteError(message='', *, context=None)

Bases: SartoriusSinkError

The backing store rejected a write.

Wraps the underlying driver exception (sqlite3, asyncpg, pyarrow) so downstream error handlers don't need to import optional dependencies. The original exception is preserved via raise ... from original so tracebacks remain intact.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusTimeoutError

SartoriusTimeoutError(message='', *, context=None)

Bases: SartoriusTransportError

A transport read or write timed out.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusTransientTransportError

SartoriusTransientTransportError(
    message="", *, context=None
)

Bases: SartoriusTransportError

Transport-layer hiccup that is safe to retry without reopening.

Raised in the cold-open window when the device is still settling and a read returns 0 bytes (transport layer) or the first frame arrives short of MIN_FRAME_SIZE (protocol layer underrun). Callers may retry the same operation up to 3 times before escalating to :class:SartoriusConnectionError; :func:sartoriuslib.open_device swallows up to 3 inside the first identify so cold-open is invisible to most callers.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusTransportError

SartoriusTransportError(message='', *, context=None)

Bases: SartoriusError

I/O-layer error from the serial transport.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusUnsupportedCommandError

SartoriusUnsupportedCommandError(
    message="", *, context=None
)

Bases: SartoriusCapabilityError, SartoriusCommandRejectedError

Device returned xBPI err 0x04 (unsupported/unknown opcode).

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusValidationError

SartoriusValidationError(message='', *, context=None)

Bases: SartoriusConfigurationError

Request validation failed before I/O.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

SartoriusValueOutOfRangeError

SartoriusValueOutOfRangeError(message='', *, context=None)

Bases: SartoriusCapabilityError, SartoriusCommandRejectedError

Device returned xBPI err 0x03 (value out of range).

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT

UnknownUnitError

UnknownUnitError(message='', *, context=None)

Bases: SartoriusConfigurationError

The unit code is not recognised.

Source code in src/sartoriuslib/errors.py
def __init__(self, message: str = "", *, context: ErrorContext | None = None) -> None:
    super().__init__(message)
    self.context = context if context is not None else _EMPTY_CONTEXT