Valkyrian Labs logo

Errors and retries

Exceptions, what is retried and what never is.

Errors and retries

forti-api reports problems as exceptions derived from fortiapi::Error and never writes to stdout or stderr.

ExceptionWhenDetails
fortiapi::ApiErrorThe device answered with an error (HTTP 400 or above, or an error envelope)http_status(), fortios_error() (for example -3, entry not found), body() with the CLI messages, not_found()
fortiapi::TransportErrorNo reply: DNS, connect, TLS, timeoutcode() (libcurl code), before_send()
fortiapi::DecodeErrorA reply that does not match the expected shapenames the endpoint and attribute path
1try {2    firewall::Addresses::add(host);3} catch (const fortiapi::ApiError& error) {4    // what() includes the HTTP status, the FortiOS error code and the CLI messages, e.g.5    // "POST cmdb/dnsfilter/profile: HTTP 500, FortiOS error -3; entry not found in datasource"6    std::cerr << error.what() << "\n";7}

find() returns std::nullopt for a missing object instead of throwing; get() throws an ApiError whose not_found() is true.

Retries

  • Reads (GET) are retried on transport errors and on HTTP 429, 502, 503 and 504, with exponential backoff (Config::retries, Config::retry_backoff).
  • Writes (POST, PUT, DELETE) are retried only when the connection failed before anything was sent (DNS, connect or TLS handshake). Once a write may have reached the device it is never repeated.
  • HTTP 401 is never retried: FortiOS locks the API out (HTTP 429) after repeated authentication failures.

Partial creates

FortiOS creates are not transactional. When an attribute is rejected after the object was created, the request fails with an ApiError and the object still exists on the device. forti-api does not clean up behind your back; check with contains() and delete it if that is what you want.

Tracing

Config::on_exchange receives every request and reply (method, URL, bodies, status, timing). The API key travels in a header that is not part of the record.