Errors and retries
How failed requests are reported, and which requests are retried.
Errors
A call that fails with an HTTP error status throws an ApiException.
try
{
await client.LookupIPAsync(IPAddress.Parse("8.8.8.8"));
}
catch (ApiException e) when (e.StatusCode == 402)
{
Console.WriteLine("out of lookup credits");
}
catch (ApiException e) when (e.StatusCode == 429 && e.RetryAfter is { } retryAfter)
{
Console.WriteLine($"rate limited, retry after {retryAfter}");
}
catch (ApiException e) when (e.StatusCode == 429)
{
Console.WriteLine("monthly request budget spent");
}
catch (ApiException e)
{
Console.WriteLine($"request {e.RequestId} failed: {e.Detail ?? e.Title}");
}
catch (ApiConnectionException e)
{
Console.WriteLine($"no response: {e.Message}");
}
- Transport and decoding: A call that gets no response throws an
ApiConnectionException, or its subclassApiTimeoutExceptionwhenSynthientClientOptions.Timeoutor the default handler's 10-second connect timeout elapses. A response the SDK cannot decode throws aSynthientException, the base type of all of them. - Cancellation: Cancelling a call's token throws
OperationCanceledException. - Response hook: An exception thrown by
ResponseHookfails the call unchanged. - Before a request: Invalid arguments, such as an IPv6 address with a scope ID or a malformed export ID, throw an
ArgumentExceptionbefore any request is sent. - Disposal: Calls on a disposed client throw
ObjectDisposedException. See Dispose.
See Errors for what each status means.
SynthientException
The base type of the exceptions the SDK throws for failed API calls.
public class SynthientException : Exception
public SynthientException(string message)
public SynthientException(string message, Exception? innerException)
- Thrown when: It is thrown directly when the API returns a response the SDK cannot use, such as a malformed body, a body or stream event larger than 16 MiB, or an export listing that repeats a cursor.
ApiException
The API returned an error status.
public sealed class ApiException : SynthientException
public ApiException(int statusCode, Exception? innerException = null)
- Name
StatusCode- Type
- int
- Description
The HTTP status code.
- Name
Type- Type
- string?
- Description
The problem type URI, such as
"https://docs.synthient.com/errors/validation-failed".
- Name
Title- Type
- string?
- Description
A short summary of the problem.
- Name
Detail- Type
- string?
- Description
A description of this occurrence of the problem. For the plain-text errors that the API's edge returns, such as a 401 for a malformed key, it is the response body.
- Name
Errors- Type
- IReadOnlyDictionary<string, IReadOnlyList<string>>
- Description
Validation errors by request field, from a 422 response. It is empty when there are none.
- Name
RequestId- Type
- string?
- Description
The ID of the request, for Synthient support. It comes from the
X-Request-Idheader, or the problem'sinstancewhen the header is absent or empty.
- Name
RetryAfter- Type
- TimeSpan?
- Description
How long the server asked the client to wait before retrying, or
nullif the response had no validRetry-Afterheader.
- Name
Headers- Type
- IReadOnlyDictionary<string, IReadOnlyList<string>>
- Description
The response headers, with case-insensitive names. It is empty for an error from the export storage host.
- Logging:
Messageholds only the status, such as403 Forbidden, so it is safe to log. - Untrusted fields: The other properties contain untrusted server data and can include sensitive information, so inspect them explicitly.
- Unwrapping:
InnerExceptionis the exception that interrupted reading the error body, if any. - Testing: The constructor is public so applications can stub the SDK in their own tests, and the other properties can be set with an object initializer.
ApiConnectionException
No response arrived: the connection failed or was closed, or reading the response failed.
public class ApiConnectionException : SynthientException
public ApiConnectionException(string message, Exception? innerException)
- Processing: The server may still have processed the request.
- Unwrapping:
InnerExceptionholds the underlying failure.
ApiTimeoutException
SynthientClientOptions.Timeout elapsed while waiting on the network, or the default handler's 10-second connect timeout elapsed.
public sealed class ApiTimeoutException : ApiConnectionException
public ApiTimeoutException(string message, Exception? innerException)
- Cancellation: Cancelling a call's token throws
OperationCanceledExceptioninstead.
Retries and timeouts
Which calls are retried:
- Account and feed requests are retried on 500, 502, 503, and 504 responses, on 429 responses that carry
Retry-After, and when no response arrives because the connection failed (other than a TLS error) or a timeout elapsed:SynthientClientOptions.Timeoutwhile waiting for the response headers, or the default handler's 10-second connect timeout. - Billed lookups (
LookupIPAsync,LookupIPsAsync, andLookupDomainAsync) andCheckHealthAsyncare retried only on 429 responses that carryRetry-After, which the rate limiter returns before doing any work, so a lookup is never sent twice after the API may have charged for it. - A 429 without
Retry-Aftermeans the key's monthly request budget is spent or it has too many open streams, so it is not retried. - A failure while reading a response body is never retried.
- Downloads from the export storage host are never retried.
How retries wait:
- Retries back off exponentially from one second, doubling up to 30 seconds, with 25% jitter.
- The wait is never shorter than
Retry-After. - A
Retry-Afterlonger than a minute throws the response'sApiExceptioninstead of retrying. - Cancelling the call's token ends the wait.
- Set the retry count with
SynthientClientOptions.MaxRetries.
SynthientClientOptions.Timeout, 60 seconds by default, bounds each wait on the network rather than a whole call, so streams and downloads are not cut off. The client's own handler also gives up on a connection attempt after 10 seconds. Bound a whole call with its cancellation token:
using var cancel = new CancellationTokenSource(TimeSpan.FromSeconds(10));
var info = await client.LookupIPAsync(IPAddress.Parse("8.8.8.8"), cancel.Token);
Console.WriteLine(info.IP);