Skip to content

Errors

Every error the SDK throws extends DinioError. Calls never resolve to null or undefined in place of an error.

Class When
DinioApiError The API refused the request. Has status, code, message, details.
DinioInputError The SDK rejected an argument before sending anything.
DinioNotFoundError A by-name function found nothing; the message lists what exists.
DinioNetworkError The API could not be reached (DNS, TLS, offline). The original error is its cause.
DinioTimeoutError No full response within timeoutMs.
DinioAbortError Your AbortSignal aborted the call.
DinioDecodeError The response was not the API’s — e.g. a proxy error page. Has status, bodySnippet.

DinioApiError.code is the API’s error.code: NOT_FOUND, CONFLICT, MANAGED_RECORD, MANAGED_CERTIFICATE, VALIDATION, FORBIDDEN, SUSPENDED, UNAUTHENTICATED, INVALID_API_KEY, INVALID_TOKEN, BAD_REQUEST, PAYLOAD_TOO_LARGE, IMPORT_FAILED, DISCOVERY_FAILED, AUDIT_DISABLED, TELEMETRY_DISABLED, NO_SUBSCRIPTION, PLAN_LIMIT, PLAN_NOT_AVAILABLE, PLAN_DOWNGRADE_BLOCKED, LIST_LIMIT, IMPORT_RUNNING, IMPORT_INTERRUPTED, FETCH_FAILED, SOURCE_MANAGED, INTERNAL, or a newer code as a plain string. details carries the violated rules on 409 and 422.

import { DinioApiError } from '@dinio/sdk';
try {
await dinio.zones.create('example.com');
} catch (err) {
if (err instanceof DinioApiError && err.code === 'CONFLICT') {
// the zone already exists
} else {
throw err;
}
}
try {
await dinio.records.create({ zone_id, name: 'www.example.com', type: 'CNAME', data: 'example.net' });
} catch (err) {
if (err instanceof DinioApiError && err.status === 409) {
console.error(err.message, err.details); // every violated zone rule
}
throw err;
}

For MANAGED_RECORD and MANAGED_CERTIFICATE, details is a ManagedDetails ({ managed_by, name }): what keeps the resource, and the redirect’s name.

import { DinioApiError, type ManagedDetails } from '@dinio/sdk';
try {
await dinio.records.delete(valueId);
} catch (err) {
if (err instanceof DinioApiError && err.code === 'MANAGED_RECORD') {
const { managed_by, name } = err.details as ManagedDetails;
console.error(`kept by ${managed_by} for ${name} — delete the redirect instead`);
} else {
throw err;
}
}

For IMPORT_FAILED, importReport(err) returns the import report — what was refused, by line or entry — and undefined for any other error:

import { importReport } from '@dinio/sdk';
try {
await dinio.zones.import('example.com', 'bind', zoneFile);
} catch (err) {
const report = importReport(err);
if (!report) throw err;
for (const issue of report.records.errors) console.error(`line ${issue.ref}: ${issue.message}`);
}

For the package errors, planLimit(err) returns { key, limit, used?, requested? } of a PLAN_LIMIT, and subscriptionLine(err) the line ('auth' or 'filter') of a NO_SUBSCRIPTION; both return undefined for any other error:

import { planLimit, subscriptionLine } from '@dinio/sdk';
try {
await dinio.clientIdentities.create({ id_type: 'Path', name: 'tablet' });
} catch (err) {
const limit = planLimit(err);
const line = subscriptionLine(err);
if (limit) console.error(`${limit.key}: ${limit.used ?? limit.requested} of ${limit.limit}`);
else if (line) console.error(`choose a ${line} package first`);
else throw err;
}

instanceof works for every class. Each error also has a name ("DinioApiError", …), for the rare case of two copies of the package in one bundle.