"""Typed error hierarchy rooted in IngestError. STABILITY CONTRACT: `IngestError.code` is the machine-readable API for distinguishing sub-causes — consumers assert on it, never on message wording. Codes listed in the class docstrings below are stable across releases; message text is NOT stable and may improve freely. """ from __future__ import annotations class IngestError(Exception): """Base class for every error raised by this library. Carries a stable, machine-readable `code` naming the sub-cause (see the subclass docstrings for the registry). Errors constructed without an explicit code carry `"unspecified"`. """ def __init__(self, message: str, *, code: str = "unspecified") -> None: super().__init__(message) self.code = code class ManifestError(IngestError): """The manifest failed fail-fast schema validation (ingest-spec §4). Codes: - `manifest_unreadable` — the manifest file cannot be read - `manifest_invalid_json` — the bytes are not valid UTF-8 JSON - `manifest_version_unsupported` — manifest_version is not the integer 1 - `manifest_schema` — a generic shape violation (wrong type, missing or unknown field, empty string, bad identifier, multi-line title, non-positive max_rows, empty extractions list) - `source_type_unknown` — source.type is not 'file', 'sql', or 'http' - `credential_embedded` — source.base_url embeds userinfo credentials - `extraction_id_duplicate` — two extractions share an id - `okf_type_reserved` — an extraction claims the reserved 'verdict' layer """ class RenderError(IngestError): """A value cannot be rendered under the §5 body rules (never silent coercion). Codes: - `unsupported_cell_type` — a SQL cell is not integer/float/text/NULL """ class SourceError(IngestError): """An extraction failed against its source (ingest-spec §4, §8). Covers fail-closed path-boundary violations, missing/malformed source content, and max_rows cap violations — always typed, never a leaked OSError and never silent truncation. Codes: - `path_escape` — a path resolves outside its root directory - `source_root_missing` — the file-source root is not a directory - `source_file_missing` — the query does not resolve to a file - `csv_no_header` — the CSV has no header row - `csv_ragged_row` — a CSV row's cell count differs from the header's - `max_rows_exceeded` — an extraction exceeds max_rows (any source type) - `connection_ref_unset` — the sql connection_ref env var is not set - `database_missing` — the sql connection_ref points at a missing file - `sql_no_columns` — the sql statement produced no result columns - `sql_failed` — the sql statement failed at the database - `credential_ref_unset` — the http credential_ref env var is not set - `http_transport` — the http GET failed at transport or decode - `fence_marker_in_body` — an http body line is a code-fence marker """ class MaterializationError(IngestError): """Materialization refused or failed (ingest-spec §5). Codes: - `ingested_at_invalid` — ingested_at is not ISO-8601 UTC with a Z suffix - `collision_unstamped` — the §3 collision gate: a generated name is occupied by a file without the ingest stamp """ class NetworkGateError(IngestError): """A network source was used without the per-run opt-in flag (spec §8). Local-only default, no silent egress: the flag is a run argument — the manifest cannot grant itself network access. Codes: - `network_opt_in_missing` — an http source without allow_network=True """