Errors and retries
How failed requests are reported, and which requests are retried.
Errors
A request that fails with a non-success HTTP status returns an *APIError. Inspect it with errors.AsType:
_, err := client.LookupIP(ctx, netip.MustParseAddr("8.8.8.8"))
apiErr, ok := errors.AsType[*synthient.APIError](err)
if ok {
switch {
case apiErr.StatusCode == http.StatusPaymentRequired:
fmt.Println("out of lookup credits")
case apiErr.StatusCode == http.StatusTooManyRequests && apiErr.RetryAfter > 0:
fmt.Println("rate limited, retry after", apiErr.RetryAfter)
case apiErr.StatusCode == http.StatusTooManyRequests:
fmt.Println("monthly request budget spent")
default:
fmt.Println("request", apiErr.RequestID, "failed:", cmp.Or(apiErr.Detail, apiErr.Title))
}
}
*APIError: The API, or the storage host duringDownload, returned a non-success status.ErrMissingAPIKey:NewClientwas given an empty or all-whitespace key.- Context errors: A canceled or expired context returns an error that matches
context.Canceledorcontext.DeadlineExceededwitherrors.Is. - Transport errors: A network failure on an API request is returned as the
*url.Errorthe HTTP client reported.Downloadremoves that wrapper from storage failures to keep the presigned URL out of the message, and a failure while reading a response is wrapped with asynthient:prefix. - Other errors: An invalid argument, such as an invalid address, more than 1000 addresses, or a malformed export ID, is rejected before any request is sent, and a response that can't be decoded also fails. These are plain errors whose messages start with
synthient:.
See Errors for what each status means.
APIError
- Name
StatusCode- Type
- int
- Description
The HTTP status code of the response.
- Name
Type- Type
- string
- Description
A URI identifying the problem type, for example
"https://docs.synthient.com/errors/unauthorized".
- Name
Title- Type
- string
- Description
A short summary of the problem type.
- Name
Detail- Type
- string
- Description
Explains this occurrence of the problem. For a plain-text error, such as the edge's 401 for a malformed key, it is the response body.
- Name
Errors- Type
- map[string][]string
- Description
Maps request fields to validation messages.
- 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.
- Name
RetryAfter- Type
- time.Duration
- Description
How long the server asked the client to wait before retrying, or zero if it did not say.
- Logging:
Error()returns only the HTTP status code and its standard description, such assynthient: 402 Payment Required, so it is safe to log. - Untrusted fields:
Title,Detail,Errors, andRequestIDcontain untrusted server data and may include sensitive information, so inspect them explicitly. - Unwrapping:
Unwrap()returns the error from reading the response body, if any, which preserveserrors.Ischecks for cancellation and deadlines.
Retries and timeouts
Which requests are retried:
- Account and feed requests are retried on 500, 502, 503, and 504 responses, on transport errors other than TLS certificate failures and HTTP responses to HTTPS requests, and on 429 responses that carry
Retry-After. A failure while reading a response body is not retried, and neither is the requestDownloadmakes to the storage host for the file itself. - 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 as billed lookups, 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 returned without a retry.
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, or a wait that would pass the context deadline, returns the response instead of retrying. - Waits stop when the context is done.
- The client retries a failed request up to twice by default. Set the count with
WithMaxRetries, where 0 disables retries.
The client sets no overall request timeout, because streams and downloads are long-lived. Bound calls with a context deadline instead:
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
info, err := client.LookupIP(ctx, netip.MustParseAddr("8.8.8.8"))
if err != nil {
log.Fatal(err)
}
fmt.Println(info.IP)
The default HTTP client waits at most one minute for response headers.