alicatlib.errors¶
Typed exception hierarchy. Every exception raised by the library is a
subclass of AlicatError and carries a structured ErrorContext.
See Design §5.17 and
Troubleshooting for remediation guidance by
error type.
alicatlib.errors ¶
Typed error hierarchy for :mod:alicatlib.
Every exception raised by the library is a subclass of :class:AlicatError
and carries a structured :class:ErrorContext. The context is deliberately a
typed dataclass (not **kwargs) so IDEs and mypy --strict can reason
about it, and so rendering is consistent across tracebacks.
Design reference: docs/design.md §5.17.
AlicatCapabilityError ¶
Bases: AlicatError
The device cannot perform the requested command.
Source code in src/alicatlib/errors.py
AlicatCommandRejectedError ¶
Bases: AlicatProtocolError
The device replied with its error marker (? / similar).
Source code in src/alicatlib/errors.py
AlicatConfigurationError ¶
AlicatConnectionError ¶
Bases: AlicatTransportError
Connection could not be established or was lost.
Source code in src/alicatlib/errors.py
AlicatDiscoveryError ¶
AlicatError ¶
Bases: Exception
Base class for every exception raised by :mod:alicatlib.
Carries a typed :class:ErrorContext. The message is the human-readable
summary; the context is the machine-readable detail.
Source code in src/alicatlib/errors.py
with_context ¶
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).
Allocates a fresh instance via cls.__new__ and copies attribute
state directly. Avoids re-invoking __init__ — many subclasses
(AlicatMediumMismatchError, AlicatFirmwareError,
UnknownGasError and friends) have bespoke keyword-only
signatures that don't accept (message, *, context=), and
copy.copy would silently dispatch through them via
:meth:Exception.__reduce__.
Source code in src/alicatlib/errors.py
AlicatFirmwareError ¶
AlicatFirmwareError(
*,
command,
reason,
actual=None,
required_min=None,
required_max=None,
required_families=None,
context=None,
)
Bases: AlicatCapabilityError
The device's firmware version is outside the command's supported range.
Source code in src/alicatlib/errors.py
AlicatMediumMismatchError ¶
Bases: AlicatConfigurationError
A command's declared medium doesn't intersect the device's configured medium.
Raised pre-I/O from :class:alicatlib.devices.session.Session at the
media gate (design §5.4, §5.9a). The typical shape: calling
:meth:Device.gas on a liquid-only device, or
:meth:Device.fluid on a gas-only device. The error carries the
mismatch in :attr:ErrorContext.device_media and
:attr:ErrorContext.command_media and points at the remediation
API in its message.
Source code in src/alicatlib/errors.py
AlicatMissingHardwareError ¶
Bases: AlicatCapabilityError
The device lacks hardware the command requires.
Raised from :class:alicatlib.devices.session.Session before any
I/O, using the :class:alicatlib.commands.base.Capability bits declared
on the :class:alicatlib.commands.base.Command spec. More useful than
letting the device silently respond ? — tells the caller exactly
which capability is missing (BAROMETER, MULTI_VALVE,
ANALOG_INPUT, ...). See design §5.17.
Source code in src/alicatlib/errors.py
AlicatParseError ¶
Bases: AlicatProtocolError
A response could not be parsed into its typed model.
Source code in src/alicatlib/errors.py
AlicatProtocolError ¶
Bases: AlicatError
The bytes arrived but did not parse as a valid Alicat response.
Source code in src/alicatlib/errors.py
AlicatSinkDependencyError ¶
Bases: AlicatSinkError, AlicatConfigurationError
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
alicatlib[parquet] or PostgresSink without
alicatlib[postgres]. The message always names the exact extra
to install so the remediation is copy-pasteable.
Multi-inherits :class:AlicatConfigurationError 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/alicatlib/errors.py
AlicatSinkError ¶
Bases: AlicatError
Base class for errors raised by sinks (CSV, JSONL, SQLite, Parquet, Postgres).
Source code in src/alicatlib/errors.py
AlicatSinkSchemaError ¶
Bases: AlicatSinkError
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, or a Parquet writer that would need a type change mid-file.
Dropping unknown optional columns is handled by a per-sink WARN log and does not raise.
Source code in src/alicatlib/errors.py
AlicatSinkWriteError ¶
Bases: AlicatSinkError
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/alicatlib/errors.py
AlicatStreamingModeError ¶
Bases: AlicatProtocolError
A request/response command was attempted while the client was in streaming mode.
Source code in src/alicatlib/errors.py
AlicatTimeoutError ¶
Bases: AlicatTransportError
An I/O timeout expired.
A timeout is never represented as an empty successful response.
Source code in src/alicatlib/errors.py
AlicatTransportError ¶
Bases: AlicatError
Serial/TCP transport failed to move bytes.
Source code in src/alicatlib/errors.py
AlicatUnitIdMismatchError ¶
Bases: AlicatProtocolError
The response's unit ID did not match the request's.
Source code in src/alicatlib/errors.py
AlicatUnsupportedCommandError ¶
Bases: AlicatCapabilityError
The command is not supported on this device kind.
Source code in src/alicatlib/errors.py
AlicatValidationError ¶
Bases: AlicatConfigurationError
Arguments failed validation before any I/O (range checks, missing confirm).
Source code in src/alicatlib/errors.py
ErrorContext
dataclass
¶
ErrorContext(
command_name=None,
command_bytes=None,
raw_response=None,
unit_id=None,
port=None,
firmware=None,
device_kind=None,
device_media=None,
command_media=None,
elapsed_s=None,
extra=_empty_extra(),
)
Structured context attached to every :class:AlicatError.
Every field is optional so callers can build a context progressively as a command flows through layers (transport → protocol → session → command).
extra accepts any Mapping and is always frozen into a read-only
:class:types.MappingProxyType at construction. The shared empty
sentinel can therefore never be mutated through
error.context.extra[k] = v.
merged ¶
Return a new context with updates overlaid. Unknown keys go to extra.
Source code in src/alicatlib/errors.py
InvalidUnitIdError ¶
Bases: AlicatConfigurationError
A unit ID was not a single letter A — Z.
Source code in src/alicatlib/errors.py
UnknownFluidError ¶
Bases: AlicatConfigurationError
A fluid (working-liquid) name or code did not resolve against the registry.
Source code in src/alicatlib/errors.py
UnknownGasError ¶
Bases: AlicatConfigurationError
A gas name or code did not resolve against the registry.
Source code in src/alicatlib/errors.py
UnknownStatisticError ¶
Bases: AlicatConfigurationError
A statistic name or code did not resolve against the registry.
Source code in src/alicatlib/errors.py
UnknownUnitError ¶
Bases: AlicatConfigurationError
A unit name or code did not resolve against the registry.