Every error Thadam surfaces — in logs, /status, quarantine rows, diagnostics — carries one ThadamError shape: a domain and a stable snake_case code written as domain.code, a human message (never payloads or secrets), a retry class, a severity, and structured context.
Retry classes:Transient (retry with capped, jittered backoff; component Degraded), Permanent (retrying cannot help; per-event errors become rejects), Fatal (component Failed, readiness false, capture continues within policy).
Severity defaults from the retry class: Warning, Error, Critical.
Codes are stable: never reused for a different meaning, only added. The thadam-core codes are snapshot-tested; the Iceberg codes are defined in thadam-iceberg.
No connector is registered for the configured kind.
config.incompatible
A configuration or data-directory version is not supported (exit code 3 on the data-directory path; also raised for a non-loopback admin bind without a token).
config.iceberg_table_missing
The Iceberg table does not exist and create_table is false. Fatal.
Quarantine rows (error_domain, error_code, reason) with the raw bytes retained.
Reject metrics: thadam_rejects_total{sink, domain} aggregates per domain — per-code detail stays in logs and quarantine to keep label cardinality bounded.