Errors and optional dependencies

Stable exception contract for HABIT.

This module is the canonical home of HABIT’s exception hierarchy. It sits at the foundation of the layering rules: it must never import other habit modules, so every layer (kernels -> contracts -> domain -> api -> interfaces) can depend on it without creating import cycles.

The canonical definitions live in this module. New code should import from here.

NotFittedError is constructed lazily via PEP 562 __getattr__: it must subclass sklearn.exceptions.NotFittedError for sklearn interop, but importing sklearn at module scope would drag the entire scientific-Python stack into every bare import habit (this module sits on the foundation import path). The sklearn import therefore happens only on first access of the class; import habit itself stays sklearn-free.

User guide: this page. The canonical import home is habit.exceptions.

Classes

HabitError

Base exception class for all HABIT errors.

HABITAPIError

Raised when a value violates a documented public API data contract.

ConfigurationError

Raised when there is an error in the configuration (YAML or dict).

DataFormatError

Raised when input data format is invalid or unsupported.

GeometryError

Raised when image and mask physical-space geometry is incompatible.

OptionalDependencyError

Raised when a requested optional HABIT backend is not installed.

ComponentNotFoundError

Raised when a requested component (model, selector, etc.) is not found in the registry.

CompatibilityError

Raised when a saved HABIT artifact cannot be safely loaded.

ProcessingError

Raised when an error occurs during data processing or pipeline execution.

NotFittedError

Raised when a model or transformer is used before being fitted.

Public exception types (stable):

from habit.exceptions import (
    HABITAPIError,
    HabitError,
    ConfigurationError,
    DataFormatError,
    GeometryError,
    OptionalDependencyError,
    ComponentNotFoundError,
    CompatibilityError,
    ProcessingError,
    NotFittedError,
)
  • HABITAPIError — invalid API use / contract breach

  • ConfigurationError — bad config / Spec

  • DataFormatError — unreadable or ill-formed data

  • GeometryError — incompatible image/mask geometry

  • OptionalDependencyError — missing optional backend

  • ComponentNotFoundError — unknown registry name

  • CompatibilityError — version / format mismatch

  • ProcessingError — runtime processing failure

  • NotFittedError — transform before fit; single canonical class defined in habit.exceptions that subclasses sklearn.exceptions.NotFittedError, so one except clause catches HABIT estimators and sklearn pipelines alike

The canonical import home is habit.exceptions.

When each exception is raised

Exception

Typical trigger

HABITAPIError

Wrong argument types, unsupported backend name, invalid volume ndim

ConfigurationError / ConfigValidationError

YAML load failure, unknown fields (extra="forbid"), schema mismatch; CLI surfaces these via habit check-config without a traceback

DataFormatError

Directory cohort has zero complete subjects; ill-formed input tables

GeometryError

Image/mask mismatch under GeometryPolicy.STRICT (API radiomics default)

OptionalDependencyError

Missing radiomics / torch / view stacks; subclass of ImportError

ComponentNotFoundError

Unknown registry / plugin name for a domain

CompatibilityError

HabitatModel.load: bad ZIP, wrong format, newer format_version; never returns a “plausible” wrong habitat map. Also raised by CheckpointStore when strict_checkpoint_hash=True meets an incompatible fingerprint or legacy checkpoint layout

ProcessingError

Pipeline / subject failure; also default map() when any subject failed (even if the backend used continue). Pass raise_on_failure=False for soft failure

NotFittedError

Estimator transform / predict before fit

Soft-failure switches (not exceptions)

These APIs collect errors instead of raising immediately:

Important

backend.map(..., on_subject_failure="continue") isolates failures; default cohort.map(op, backend=...) still raises ProcessingError if any slot failed. Pass raise_on_failure=False (recipes / CLI) or call the backend directly for soft failure (see Fault tolerance patterns).

Probe optional stacks without importing heavy backends:

from habit.utils.runtime import is_available

if is_available("torch"):
    ...
if is_available("radiomics"):
    ...

Logger helper for scripts:

from habit.utils.runtime import setup_logger

logger = setup_logger(
    name="study",
    output_dir="out",
    log_filename="run.log",
)