Exception Hierarchy

siege_utilities follows a fail-loud-over-silent-swallow policy: when a function cannot deliver its documented output, it raises a typed exception rather than returning None, False, or an empty container that looks like a legitimate β€œno result.” This distinguishes real failures from expected empty-input paths and prevents silent data corruption in downstream pipelines.

This page catalogs the typed exceptions across the library. Every exception subclasses a standard Python exception (LookupError, ValueError, RuntimeError) so broad existing handlers continue to work.

Design principles

  1. Lookup failures are ``LookupError``. Missing chart types, missing client configs, and similar β€œyou asked for X but X doesn’t exist” cases.

  2. Parameter / input failures are ``ValueError``. Missing required parameters, invalid values, bad config shapes.

  3. Operation failures are ``RuntimeError``. Failed I/O, failed parse, failed API call β€” anything where the inputs were valid but the work did not complete.

  4. Not-found return values are preserved where semantic. list_X() returning [], lookup helpers returning None when the caller should handle absence as normal β€” these are unchanged. The rewrite only affects sites where absence was masking a transport or parse failure.

  5. Every raise chains with ``from e``. Inspect exc.__cause__ to see the underlying error (JSONDecodeError, OSError, HTTPError, etc.).

Reporting

Top-level config export / import

exception siege_utilities.reporting.ReportingConfigError[source]

Bases: RuntimeError

Raised when a reporting configuration export / import cannot complete.

__init__(*args, **kwargs)
classmethod __new__(*args, **kwargs)

Chart type registry

Client branding

Geographic

Census Bureau geocoder

exception siege_utilities.geo.census_geocoder.CensusGeocodeError[source]

Bases: RuntimeError

Raised when the Census geocoder API call fails unexpectedly.

Distinct from β€œno match” results (which return a CensusGeocodeResult with matched=False). This exception indicates an API / network / parse failure where the geocoder could not even attempt to match. Use __cause__ to inspect the underlying exception.

__init__(*args, **kwargs)
classmethod __new__(*args, **kwargs)

Before this change, geocode_single() and geocode_batch() caught all failures (network, API, parse) and returned CensusGeocodeResult(matched=False). Downstream pipelines treated unmatched rows as β€œaddress not findable” and dropped them β€” so API outages silently poisoned entire batches with fake unmatched rows. CensusGeocodeError surfaces the real cause.

Spatial data sources

Used by GovernmentDataSource (CKAN-style portals) and OpenStreetMapDataSource (Overpass API). Distinct from boundary retrieval, which has its own hierarchy below.

Boundary retrieval

exception siege_utilities.geo.boundary_result.BoundaryRetrievalError[source]

Bases: SiegeGeoError

Base exception for all boundary retrieval failures.

Inherits from SiegeGeoError so callers can catch the entire siege_utilities exception family with a single except SiegeError:. Previously this stood alone outside the documented hierarchy and slipped past except SiegeError blocks.

__init__(message, stage, context=None)[source]
classmethod __new__(*args, **kwargs)
exception siege_utilities.geo.boundary_result.BoundaryInputError[source]

Bases: BoundaryRetrievalError

Invalid input parameters (state FIPS, year, geographic level).

__init__(message, context=None)[source]
classmethod __new__(*args, **kwargs)
exception siege_utilities.geo.boundary_result.BoundaryDiscoveryError[source]

Bases: BoundaryRetrievalError

Failed to discover available boundary types or construct a URL.

__init__(message, context=None)[source]
classmethod __new__(*args, **kwargs)
exception siege_utilities.geo.boundary_result.BoundaryUrlValidationError[source]

Bases: BoundaryRetrievalError

Constructed URL is not accessible (HTTP error, timeout, etc.).

__init__(message, context=None)[source]
classmethod __new__(*args, **kwargs)
exception siege_utilities.geo.boundary_result.BoundaryDownloadError[source]

Bases: BoundaryRetrievalError

Download succeeded but the file is corrupt or not a valid zip.

__init__(message, context=None)[source]
classmethod __new__(*args, **kwargs)
exception siege_utilities.geo.boundary_result.BoundaryParseError[source]

Bases: BoundaryRetrievalError

Downloaded data could not be parsed as a shapefile/GeoDataFrame.

__init__(message, context=None)[source]
classmethod __new__(*args, **kwargs)
exception siege_utilities.geo.boundary_result.BoundaryConfigurationError[source]

Bases: BoundaryRetrievalError

Boundary type requires parameters that were not provided (e.g., state FIPS, congress number).

__init__(message, context=None)[source]
classmethod __new__(*args, **kwargs)

Migration guidance

Callers that relied on the pre-rewrite silent-swallow behavior must migrate to try/except around the new exception types.

Before:

result = registry.create_chart("unknown_type")
if result is None:
    log.warning("chart creation failed")
    return None

After:

try:
    result = registry.create_chart("unknown_type")
except UnknownChartTypeError:
    log.warning("unknown chart type")
    return None
except ChartCreationError as e:
    log.error("chart creation failed: %s", e.__cause__)
    raise

Because the exception types subclass standard Python exceptions, you can also use a single broad except LookupError: / except ValueError: / except RuntimeError: if you do not need to distinguish cases. The __cause__ attribute still gives you the original error.

Further reading

  • ../../docs/FAILURE_MODES β€” complete anti-pattern catalog

  • ../../docs/ARCHITECTURE β€” three-layer dependency model