> ## Documentation Index
> Fetch the complete documentation index at: https://instro.nominal.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Exceptions

> Handling Instro-specific errors

# Exceptions

Instro-specific errors inherit from `InstroError`.

```python theme={null}
from instro.lib.exceptions import InstroError

try:
    ...
except InstroError:
    ...
```

When a capability is unavailable, the library raises one of two distinct exceptions. They differ in *why* the capability is missing and, just as importantly, in what they inherit from.

## FeatureNotSupportedError

`FeatureNotSupportedError` (a subclass of `InstroError`) is raised by a concrete driver to declare that the connected instrument genuinely lacks a capability. For example, the `BK9115` power supply has no overcurrent protection or remote sense, so its driver raises `FeatureNotSupportedError` for those methods.

```python theme={null}
from instro.lib.exceptions import FeatureNotSupportedError

try:
    psu.set_remote_sense_enabled(True, channel=1)
except FeatureNotSupportedError:
    ...
```

Because it inherits from `InstroError`, `except InstroError` catches it.

## InstrumentNotOpenError

`InstrumentNotOpenError` (a subclass of `InstroError`) is raised when a device-touching method is called before `open()`. It points at the missing lifecycle call instead of surfacing a low-level transport or vendor-SDK error. The message names the instrument instance so you can tell which one you forgot to open.

```python theme={null}
from instro.lib.exceptions import InstrumentNotOpenError

daq = InstroDAQ(name="myDAQ", driver=...)
try:
    daq.read_analog()  # forgot to call daq.open() first
except InstrumentNotOpenError:
    ...
```

Open the instrument first — directly or via the context manager, which opens on entry and closes on exit:

```python theme={null}
with daq:
    daq.configure_analog_channel(...)
    daq.read_analog()
```

`close()` and `stop()` are tolerant teardown and never raise `InstrumentNotOpenError`. Closing an instrument that never opened, closing twice, or calling `close()` from a `finally` after a failed `open()` is safe and leaves the original error free to surface.

Because it inherits from `InstroError`, `except InstroError` catches it.

## NotImplementedError

`NotImplementedError` is the Python builtin, not an `InstroError`. Each `<Category>DriverBase` declares its optional methods to raise `NotImplementedError` by default, and a driver overrides only the ones its instrument supports. Calling an optional method on a driver that has not overridden it therefore raises `NotImplementedError`: the capability is part of the category contract, but this driver does not implement it.

Because `NotImplementedError` is a builtin, `except InstroError` does **not** catch it.

## Which one will I catch?

It depends on the driver behind the HAL:

* A driver that explicitly declares a feature absent raises `FeatureNotSupportedError`.
* A driver that has simply not overridden an optional base method raises `NotImplementedError`.

To handle both in one place, catch them together:

```python theme={null}
from instro.lib.exceptions import FeatureNotSupportedError

try:
    psu.set_overcurrent_protection_level(5.0, channel=1)
except (FeatureNotSupportedError, NotImplementedError):
    ...
```

When writing a driver, raise `FeatureNotSupportedError` when the hardware lacks the feature, and leave the inherited `NotImplementedError` in place for optional methods that are not implemented yet.
