Errors and retries
How failed requests are reported, and which requests are retried.
Errors
Every exception the SDK throws for a failed call is unchecked and extends SynthientException:
| Exception | Thrown when |
|---|---|
ApiException | The API answered with an error status. |
ApiConnectionException | No response arrived: the connection failed or was lost, or the calling thread was interrupted. After an interruption, the thread's interrupt flag is set again. getCause() holds the underlying failure. |
ApiTimeoutException | Connecting took longer than the connect timeout, or the API sent nothing for longer than the client's timeout, either while waiting for the response headers or between parts of the body. It extends ApiConnectionException. |
SynthientException | The API sent a response the SDK could not use, such as invalid JSON, a missing required field, or a body over the size limit. It is also the parent of the others. |
try {
IpInfo info = client.lookupIp("8.8.8.8");
System.out.println(info.ip());
} catch (ApiException e) {
switch (e.statusCode()) {
case 402 -> System.out.println("out of lookup credits");
case 429 -> System.out.println(
e.retryAfter() == null ? "monthly request budget spent" : "rate limited, retry after " + e.retryAfter());
default -> System.out.println(
"request " + e.requestId() + " failed: " + (e.detail().isEmpty() ? e.title() : e.detail()));
}
} catch (ApiConnectionException e) {
System.out.println("no response: " + e.getMessage());
}
- Before a request: Invalid arguments, such as an address that is not an IP literal, an invalid export ID, or a missing API key, throw
IllegalArgumentException, and no request is sent. - Invalid state: Calls on a closed client, and a second iteration of an
EventStream, throwIllegalStateException. - Downloads: Reading the
InputStreamthatdownloadreturns throwsIOException, such asjava.net.SocketTimeoutException, rather than an SDK exception.
See Errors for what each status means.
ApiException
The fields come from the problem details document in the response body when there is one.
- Name
statusCode- Type
- int
- Description
The HTTP status of the response.
- Name
type- Type
- String
- Description
The problem type URI, or an empty string.
- Name
title- Type
- String
- Description
The short problem summary, or an empty string.
- Name
detail- Type
- String
- Description
The server's explanation of the problem, or an empty string. For a plain-text error, such as the edge's 401 for a malformed key, it is the whole body.
- Name
errors- Type
- Map<String, List<String>>
- Description
Validation errors by field name, or an empty map.
- Name
requestId- Type
- String
- Description
Identifies the request when contacting Synthient support. It comes from the
X-Request-Idheader, or the problem'sinstancewhen the header is absent, and is otherwise an empty string.
- Name
retryAfter- Type
- Duration
- Description
How long the server asked the client to wait before retrying, or
nullif it did not say. A 429 without it means the monthly budget is spent or too many streams are open, so retrying will not help.
- Logging:
getMessage()holds only the status, such as403 Forbidden, so the exception is safe to log. - Untrusted fields:
type(),title(),detail(),errors(), andrequestId()contain untrusted server data, so inspect them explicitly.
Constructors
The exception constructors are public, for example to stub the SDK in tests.
public SynthientException(String message)
public SynthientException(String message, @Nullable Throwable cause)
public ApiConnectionException(String message, @Nullable Throwable cause)
public ApiTimeoutException(String message, @Nullable Throwable cause)
public ApiException(
int statusCode,
String type,
String title,
String detail,
Map<String, List<String>> errors,
String requestId,
@Nullable Duration retryAfter,
@Nullable Throwable cause)
- Input:
ApiExceptiontakes an empty string or map for a missing field, andcauseis the failure that interrupted reading the error body, if any.
Retries and timeouts
Which requests are retried:
- Account and feed requests (
account,listExports, each page ofallExports,exportMeta,downloadUrl, the API request ofdownload, andstreamconnections) are retried on 500, 502, 503, and 504 responses, on 429 responses that carryRetry-After, and on connection failures and timeouts that happen before the response arrives, other than TLS failures and interruptions. - Billed lookups (
lookupIp,lookupIps, andlookupDomain) are retried only on 429 responses that carryRetry-After, which the rate limiter returns before doing any work, so a lookup is never charged twice.healthis retried the same way, so an unhealthy API is reported at once. - A 429 without
Retry-Aftermeans the key's monthly request budget is spent or it has too many open streams, so it is thrown without a retry. - Reads: A failure while reading a response body, stream, or download is not retried, and neither is a download's transfer from the export storage host.
- OkHttp itself never resends a request.
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 instead of retrying. - Interrupting the waiting thread throws
ApiConnectionException. - Set the retry count with
maxRetrieson the builder.
The client sets no overall request timeout, because streams and downloads are long-lived. Instead, the builder's timeout, 60 seconds by default, bounds how long the client waits for the response headers and then for each part of the body, and exceeding it throws ApiTimeoutException, after any retries for account and feed requests.
try {
IpInfo info = client.lookupIp("8.8.8.8");
System.out.println(info.ip());
} catch (ApiTimeoutException e) {
System.err.println("no response in time: " + e.getMessage());
}
- Connection: Connecting has its own timeout of 10 seconds, or your
OkHttpClient's if you pass one, and running out of it also throwsApiTimeoutException. - Cancellation: Interrupting a thread that is waiting for a response or for a retry cancels the call, which throws
ApiConnectionException. A stream or download that is already reading may not notice an interrupt until more data arrives, so close it from another thread instead.