Skip to content

Errors

Every call returns dinio_sdk::Result<T>, with one error type:

pub enum Error {
/// The API refused the request.
Api(ApiError),
/// No response: DNS, TLS, connection, timeout.
Transport(reqwest::Error),
/// The response was not the API's (e.g. a proxy error page).
Decode { status: u16, snippet: String },
/// Rejected by the SDK before sending (bad URL, bad argument).
InvalidInput(String),
/// An `ops` function found nothing to act on.
NotFound(String),
}
pub struct ApiError {
pub status: u16,
pub code: ErrorCode,
pub message: String,
pub details: Option<serde_json::Value>, // e.g. violated zone rules on 409 / 422
}

ErrorCode mirrors the API’s error.code: Unauthenticated, InvalidApiKey, InvalidToken, Forbidden, Suspended, NotFound, Conflict, ManagedRecord, ManagedCertificate, Validation, ImportFailed, DiscoveryFailed, PayloadTooLarge, AuditDisabled, TelemetryDisabled, BadRequest, Internal, and Other(String) for codes this SDK version doesn’t know. It is #[non_exhaustive]: later versions add variants for new codes, so a match needs a wildcard arm.

For ManagedRecord and ManagedCertificate, ApiError::managed() returns Some((managed_by, name)) — what keeps the resource, and the redirect’s name:

match client.records().delete(value_id).await {
Err(Error::Api(err)) if err.code == ErrorCode::ManagedRecord => {
if let Some((managed_by, name)) = err.managed() {
eprintln!("kept by {managed_by} for {name}: delete the redirect instead");
}
}
other => other?,
}
use dinio_sdk::{Error, ErrorCode};
match client.zones().create("example.com").await {
Ok(created) => println!("created {}", created.value.uuid),
Err(Error::Api(err)) if err.code == ErrorCode::Conflict => println!("already exists"),
Err(err) => return Err(err.into()),
}

For the package errors, ApiError::plan_limit() returns a PlanLimit { key, limit, used, requested } for ErrorCode::PlanLimit, and ApiError::subscription_line() the line ("auth" or "filter") for ErrorCode::NoSubscription:

match client.client_identities().create(&identity).await {
Err(Error::Api(err)) if err.code == ErrorCode::PlanLimit => {
if let Some(limit) = err.plan_limit() {
eprintln!("{}: limit {}, in use {:?}", limit.key, limit.limit, limit.used);
}
}
Err(Error::Api(err)) if err.code == ErrorCode::NoSubscription => {
eprintln!("choose a {} package first", err.subscription_line().unwrap_or("?"));
}
other => { other?; }
}

For ImportFailed, ApiError::import_report() returns the import report: what was refused, by line or entry.

The SDK never panics on API or transport errors and never prints anything. Errors implement std::error::Error and Display with the API’s message, code and HTTP status:

Zone not found (NOT_FOUND, HTTP 404)